diff --git a/src/addrman.cpp b/src/addrman.cpp deleted file mode 100644 index 780edde9..00000000 --- a/src/addrman.cpp +++ /dev/null @@ -1,528 +0,0 @@ -// Copyright (c) 2012 Pieter Wuille -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "addrman.h" -#include "hash.h" - -using namespace std; - -int CAddrInfo::GetTriedBucket(const std::vector &nKey) const -{ - CDataStream ss1(SER_GETHASH, 0); - std::vector vchKey = GetKey(); - ss1 << nKey << vchKey; - uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64(); - - CDataStream ss2(SER_GETHASH, 0); - std::vector vchGroupKey = GetGroup(); - ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP); - uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64(); - return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; -} - -int CAddrInfo::GetNewBucket(const std::vector &nKey, const CNetAddr& src) const -{ - CDataStream ss1(SER_GETHASH, 0); - std::vector vchGroupKey = GetGroup(); - std::vector vchSourceGroupKey = src.GetGroup(); - ss1 << nKey << vchGroupKey << vchSourceGroupKey; - uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64(); - - CDataStream ss2(SER_GETHASH, 0); - ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP); - uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64(); - return hash2 % ADDRMAN_NEW_BUCKET_COUNT; -} - -bool CAddrInfo::IsTerrible(int64 nNow) const -{ - if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute - return false; - - if (nTime > nNow + 10*60) // came in a flying DeLorean - return true; - - if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*86400) // not seen in over a month - return true; - - if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success - return true; - - if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*86400 && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week - return true; - - return false; -} - -double CAddrInfo::GetChance(int64 nNow) const -{ - double fChance = 1.0; - - int64 nSinceLastSeen = nNow - nTime; - int64 nSinceLastTry = nNow - nLastTry; - - if (nSinceLastSeen < 0) nSinceLastSeen = 0; - if (nSinceLastTry < 0) nSinceLastTry = 0; - - fChance *= 600.0 / (600.0 + nSinceLastSeen); - - // deprioritize very recent attempts away - if (nSinceLastTry < 60*10) - fChance *= 0.01; - - // deprioritize 50% after each failed attempt - for (int n=0; n::iterator it = mapAddr.find(addr); - if (it == mapAddr.end()) - return NULL; - if (pnId) - *pnId = (*it).second; - std::map::iterator it2 = mapInfo.find((*it).second); - if (it2 != mapInfo.end()) - return &(*it2).second; - return NULL; -} - -CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId) -{ - int nId = nIdCount++; - mapInfo[nId] = CAddrInfo(addr, addrSource); - mapAddr[addr] = nId; - mapInfo[nId].nRandomPos = vRandom.size(); - vRandom.push_back(nId); - if (pnId) - *pnId = nId; - return &mapInfo[nId]; -} - -void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) -{ - if (nRndPos1 == nRndPos2) - return; - - assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size()); - - int nId1 = vRandom[nRndPos1]; - int nId2 = vRandom[nRndPos2]; - - assert(mapInfo.count(nId1) == 1); - assert(mapInfo.count(nId2) == 1); - - mapInfo[nId1].nRandomPos = nRndPos2; - mapInfo[nId2].nRandomPos = nRndPos1; - - vRandom[nRndPos1] = nId2; - vRandom[nRndPos2] = nId1; -} - -int CAddrMan::SelectTried(int nKBucket) -{ - std::vector &vTried = vvTried[nKBucket]; - - // random shuffle the first few elements (using the entire list) - // find the least recently tried among them - int64 nOldest = -1; - int nOldestPos = -1; - for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++) - { - int nPos = GetRandInt(vTried.size() - i) + i; - int nTemp = vTried[nPos]; - vTried[nPos] = vTried[i]; - vTried[i] = nTemp; - assert(nOldest == -1 || mapInfo.count(nTemp) == 1); - if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) { - nOldest = nTemp; - nOldestPos = nPos; - } - } - - return nOldestPos; -} - -int CAddrMan::ShrinkNew(int nUBucket) -{ - assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size()); - std::set &vNew = vvNew[nUBucket]; - - // first look for deletable items - for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) - { - assert(mapInfo.count(*it)); - CAddrInfo &info = mapInfo[*it]; - if (info.IsTerrible()) - { - if (--info.nRefCount == 0) - { - SwapRandom(info.nRandomPos, vRandom.size()-1); - vRandom.pop_back(); - mapAddr.erase(info); - mapInfo.erase(*it); - nNew--; - } - vNew.erase(it); - return 0; - } - } - - // otherwise, select four randomly, and pick the oldest of those to replace - int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())}; - int nI = 0; - int nOldest = -1; - for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) - { - if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3]) - { - assert(nOldest == -1 || mapInfo.count(*it) == 1); - if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime) - nOldest = *it; - } - nI++; - } - assert(mapInfo.count(nOldest) == 1); - CAddrInfo &info = mapInfo[nOldest]; - if (--info.nRefCount == 0) - { - SwapRandom(info.nRandomPos, vRandom.size()-1); - vRandom.pop_back(); - mapAddr.erase(info); - mapInfo.erase(nOldest); - nNew--; - } - vNew.erase(nOldest); - - return 1; -} - -void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin) -{ - assert(vvNew[nOrigin].count(nId) == 1); - - // remove the entry from all new buckets - for (std::vector >::iterator it = vvNew.begin(); it != vvNew.end(); it++) - { - if ((*it).erase(nId)) - info.nRefCount--; - } - nNew--; - - assert(info.nRefCount == 0); - - // what tried bucket to move the entry to - int nKBucket = info.GetTriedBucket(nKey); - std::vector &vTried = vvTried[nKBucket]; - - // first check whether there is place to just add it - if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) - { - vTried.push_back(nId); - nTried++; - info.fInTried = true; - return; - } - - // otherwise, find an item to evict - int nPos = SelectTried(nKBucket); - - // find which new bucket it belongs to - assert(mapInfo.count(vTried[nPos]) == 1); - int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey); - std::set &vNew = vvNew[nUBucket]; - - // remove the to-be-replaced tried entry from the tried set - CAddrInfo& infoOld = mapInfo[vTried[nPos]]; - infoOld.fInTried = false; - infoOld.nRefCount = 1; - // do not update nTried, as we are going to move something else there immediately - - // check whether there is place in that one, - if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE) - { - // if so, move it back there - vNew.insert(vTried[nPos]); - } else { - // otherwise, move it to the new bucket nId came from (there is certainly place there) - vvNew[nOrigin].insert(vTried[nPos]); - } - nNew++; - - vTried[nPos] = nId; - // we just overwrote an entry in vTried; no need to update nTried - info.fInTried = true; - return; -} - -void CAddrMan::Good_(const CService &addr, int64 nTime) -{ -// printf("Good: addr=%s\n", addr.ToString().c_str()); - - int nId; - CAddrInfo *pinfo = Find(addr, &nId); - - // if not found, bail out - if (!pinfo) - return; - - CAddrInfo &info = *pinfo; - - // check whether we are talking about the exact same CService (including same port) - if (info != addr) - return; - - // update info - info.nLastSuccess = nTime; - info.nLastTry = nTime; - info.nTime = nTime; - info.nAttempts = 0; - - // if it is already in the tried set, don't do anything else - if (info.fInTried) - return; - - // find a bucket it is in now - int nRnd = GetRandInt(vvNew.size()); - int nUBucket = -1; - for (unsigned int n = 0; n < vvNew.size(); n++) - { - int nB = (n+nRnd) % vvNew.size(); - std::set &vNew = vvNew[nB]; - if (vNew.count(nId)) - { - nUBucket = nB; - break; - } - } - - // if no bucket is found, something bad happened; - // TODO: maybe re-add the node, but for now, just bail out - if (nUBucket == -1) return; - - printf("Moving %s to tried\n", addr.ToString().c_str()); - - // move nId to the tried tables - MakeTried(info, nId, nUBucket); -} - -bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty) -{ - if (!addr.IsRoutable()) - return false; - - bool fNew = false; - int nId; - CAddrInfo *pinfo = Find(addr, &nId); - - if (pinfo) - { - // periodically update nTime - bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); - int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); - if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) - pinfo->nTime = max((int64)0, addr.nTime - nTimePenalty); - - // add services - pinfo->nServices |= addr.nServices; - - // do not update if no new information is present - if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime)) - return false; - - // do not update if the entry was already in the "tried" table - if (pinfo->fInTried) - return false; - - // do not update if the max reference count is reached - if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS) - return false; - - // stochastic test: previous nRefCount == N: 2^N times harder to increase it - int nFactor = 1; - for (int n=0; nnRefCount; n++) - nFactor *= 2; - if (nFactor > 1 && (GetRandInt(nFactor) != 0)) - return false; - } else { - pinfo = Create(addr, source, &nId); - pinfo->nTime = max((int64)0, (int64)pinfo->nTime - nTimePenalty); -// printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0); - nNew++; - fNew = true; - } - - int nUBucket = pinfo->GetNewBucket(nKey, source); - std::set &vNew = vvNew[nUBucket]; - if (!vNew.count(nId)) - { - pinfo->nRefCount++; - if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE) - ShrinkNew(nUBucket); - vvNew[nUBucket].insert(nId); - } - return fNew; -} - -void CAddrMan::Attempt_(const CService &addr, int64 nTime) -{ - CAddrInfo *pinfo = Find(addr); - - // if not found, bail out - if (!pinfo) - return; - - CAddrInfo &info = *pinfo; - - // check whether we are talking about the exact same CService (including same port) - if (info != addr) - return; - - // update info - info.nLastTry = nTime; - info.nAttempts++; -} - -CAddress CAddrMan::Select_(int nUnkBias) -{ - if (size() == 0) - return CAddress(); - - double nCorTried = sqrt(nTried) * (100.0 - nUnkBias); - double nCorNew = sqrt(nNew) * nUnkBias; - if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried) - { - // use a tried node - double fChanceFactor = 1.0; - while(1) - { - int nKBucket = GetRandInt(vvTried.size()); - std::vector &vTried = vvTried[nKBucket]; - if (vTried.size() == 0) continue; - int nPos = GetRandInt(vTried.size()); - assert(mapInfo.count(vTried[nPos]) == 1); - CAddrInfo &info = mapInfo[vTried[nPos]]; - if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30)) - return info; - fChanceFactor *= 1.2; - } - } else { - // use a new node - double fChanceFactor = 1.0; - while(1) - { - int nUBucket = GetRandInt(vvNew.size()); - std::set &vNew = vvNew[nUBucket]; - if (vNew.size() == 0) continue; - int nPos = GetRandInt(vNew.size()); - std::set::iterator it = vNew.begin(); - while (nPos--) - it++; - assert(mapInfo.count(*it) == 1); - CAddrInfo &info = mapInfo[*it]; - if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30)) - return info; - fChanceFactor *= 1.2; - } - } -} - -#ifdef DEBUG_ADDRMAN -int CAddrMan::Check_() -{ - std::set setTried; - std::map mapNew; - - if (vRandom.size() != nTried + nNew) return -7; - - for (std::map::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) - { - int n = (*it).first; - CAddrInfo &info = (*it).second; - if (info.fInTried) - { - - if (!info.nLastSuccess) return -1; - if (info.nRefCount) return -2; - setTried.insert(n); - } else { - if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return -3; - if (!info.nRefCount) return -4; - mapNew[n] = info.nRefCount; - } - if (mapAddr[info] != n) return -5; - if (info.nRandomPos<0 || info.nRandomPos>=vRandom.size() || vRandom[info.nRandomPos] != n) return -14; - if (info.nLastTry < 0) return -6; - if (info.nLastSuccess < 0) return -8; - } - - if (setTried.size() != nTried) return -9; - if (mapNew.size() != nNew) return -10; - - for (int n=0; n &vTried = vvTried[n]; - for (std::vector::iterator it = vTried.begin(); it != vTried.end(); it++) - { - if (!setTried.count(*it)) return -11; - setTried.erase(*it); - } - } - - for (int n=0; n &vNew = vvNew[n]; - for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) - { - if (!mapNew.count(*it)) return -12; - if (--mapNew[*it] == 0) - mapNew.erase(*it); - } - } - - if (setTried.size()) return -13; - if (mapNew.size()) return -15; - - return 0; -} -#endif - -void CAddrMan::GetAddr_(std::vector &vAddr) -{ - int nNodes = ADDRMAN_GETADDR_MAX_PCT*vRandom.size()/100; - if (nNodes > ADDRMAN_GETADDR_MAX) - nNodes = ADDRMAN_GETADDR_MAX; - - // perform a random shuffle over the first nNodes elements of vRandom (selecting from all) - for (int n = 0; n nUpdateInterval) - info.nTime = nTime; -} diff --git a/src/addrman.h b/src/addrman.h deleted file mode 100644 index 7af6afd7..00000000 --- a/src/addrman.h +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright (c) 2012 Pieter Wuille -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOIN_ADDRMAN -#define _BITCOIN_ADDRMAN 1 - -#include "netbase.h" -#include "protocol.h" -#include "util.h" -#include "sync.h" - - -#include -#include - -#include - - -/** Extended statistics about a CAddress */ -class CAddrInfo : public CAddress -{ -private: - // where knowledge about this address first came from - CNetAddr source; - - // last successful connection by us - int64 nLastSuccess; - - // last try whatsoever by us: - // int64 CAddress::nLastTry - - // connection attempts since last successful attempt - int nAttempts; - - // reference count in new sets (memory only) - int nRefCount; - - // in tried set? (memory only) - bool fInTried; - - // position in vRandom - int nRandomPos; - - friend class CAddrMan; - -public: - - IMPLEMENT_SERIALIZE( - CAddress* pthis = (CAddress*)(this); - READWRITE(*pthis); - READWRITE(source); - READWRITE(nLastSuccess); - READWRITE(nAttempts); - ) - - void Init() - { - nLastSuccess = 0; - nLastTry = 0; - nAttempts = 0; - nRefCount = 0; - fInTried = false; - nRandomPos = -1; - } - - CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) - { - Init(); - } - - CAddrInfo() : CAddress(), source() - { - Init(); - } - - // Calculate in which "tried" bucket this entry belongs - int GetTriedBucket(const std::vector &nKey) const; - - // Calculate in which "new" bucket this entry belongs, given a certain source - int GetNewBucket(const std::vector &nKey, const CNetAddr& src) const; - - // Calculate in which "new" bucket this entry belongs, using its default source - int GetNewBucket(const std::vector &nKey) const - { - return GetNewBucket(nKey, source); - } - - // Determine whether the statistics about this entry are bad enough so that it can just be deleted - bool IsTerrible(int64 nNow = GetAdjustedTime()) const; - - // Calculate the relative chance this entry should be given when selecting nodes to connect to - double GetChance(int64 nNow = GetAdjustedTime()) const; - -}; - -// Stochastic address manager -// -// Design goals: -// * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound. -// * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat. -// * Make sure no (localized) attacker can fill the entire table with his nodes/addresses. -// -// To that end: -// * Addresses are organized into buckets. -// * Address that have not yet been tried go into 256 "new" buckets. -// * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random -// * The actual bucket is chosen from one of these, based on the range the address itself is located. -// * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that -// are seen frequently. The chance for increasing this multiplicity decreases exponentially. -// * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen -// ones) is removed from it first. -// * Addresses of nodes that are known to be accessible go into 64 "tried" buckets. -// * Each address range selects at random 4 of these buckets. -// * The actual bucket is chosen from one of these, based on the full address. -// * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently -// tried ones) is evicted from it, back to the "new" buckets. -// * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not -// be observable by adversaries. -// * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive) -// consistency checks for the entire data structure. - -// total number of buckets for tried addresses -#define ADDRMAN_TRIED_BUCKET_COUNT 64 - -// maximum allowed number of entries in buckets for tried addresses -#define ADDRMAN_TRIED_BUCKET_SIZE 64 - -// total number of buckets for new addresses -#define ADDRMAN_NEW_BUCKET_COUNT 256 - -// maximum allowed number of entries in buckets for new addresses -#define ADDRMAN_NEW_BUCKET_SIZE 64 - -// over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread -#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4 - -// over how many buckets entries with new addresses originating from a single group are spread -#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32 - -// in how many buckets for entries with new addresses a single address may occur -#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4 - -// how many entries in a bucket with tried addresses are inspected, when selecting one to replace -#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4 - -// how old addresses can maximally be -#define ADDRMAN_HORIZON_DAYS 30 - -// after how many failed attempts we give up on a new node -#define ADDRMAN_RETRIES 3 - -// how many successive failures are allowed ... -#define ADDRMAN_MAX_FAILURES 10 - -// ... in at least this many days -#define ADDRMAN_MIN_FAIL_DAYS 7 - -// the maximum percentage of nodes to return in a getaddr call -#define ADDRMAN_GETADDR_MAX_PCT 23 - -// the maximum number of nodes to return in a getaddr call -#define ADDRMAN_GETADDR_MAX 2500 - -/** Stochastical (IP) address manager */ -class CAddrMan -{ -private: - // critical section to protect the inner data structures - mutable CCriticalSection cs; - - // secret key to randomize bucket select with - std::vector nKey; - - // last used nId - int nIdCount; - - // table with information about all nIds - std::map mapInfo; - - // find an nId based on its network address - std::map mapAddr; - - // randomly-ordered vector of all nIds - std::vector vRandom; - - // number of "tried" entries - int nTried; - - // list of "tried" buckets - std::vector > vvTried; - - // number of (unique) "new" entries - int nNew; - - // list of "new" buckets - std::vector > vvNew; - -protected: - - // Find an entry. - CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL); - - // find an entry, creating it if necessary. - // nTime and nServices of found node is updated, if necessary. - CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL); - - // Swap two elements in vRandom. - void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); - - // Return position in given bucket to replace. - int SelectTried(int nKBucket); - - // Remove an element from a "new" bucket. - // This is the only place where actual deletes occur. - // They are never deleted while in the "tried" table, only possibly evicted back to the "new" table. - int ShrinkNew(int nUBucket); - - // Move an entry from the "new" table(s) to the "tried" table - // @pre vvUnkown[nOrigin].count(nId) != 0 - void MakeTried(CAddrInfo& info, int nId, int nOrigin); - - // Mark an entry "good", possibly moving it from "new" to "tried". - void Good_(const CService &addr, int64 nTime); - - // Add an entry to the "new" table. - bool Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty); - - // Mark an entry as attempted to connect. - void Attempt_(const CService &addr, int64 nTime); - - // Select an address to connect to. - // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100) - CAddress Select_(int nUnkBias); - -#ifdef DEBUG_ADDRMAN - // Perform consistency check. Returns an error code or zero. - int Check_(); -#endif - - // Select several addresses at once. - void GetAddr_(std::vector &vAddr); - - // Mark an entry as currently-connected-to. - void Connected_(const CService &addr, int64 nTime); - -public: - - IMPLEMENT_SERIALIZE - (({ - // serialized format: - // * version byte (currently 0) - // * nKey - // * nNew - // * nTried - // * number of "new" buckets - // * all nNew addrinfos in vvNew - // * all nTried addrinfos in vvTried - // * for each bucket: - // * number of elements - // * for each element: index - // - // Notice that vvTried, mapAddr and vVector are never encoded explicitly; - // they are instead reconstructed from the other information. - // - // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, - // otherwise it is reconstructed as well. - // - // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports - // changes to the ADDRMAN_ parameters without breaking the on-disk structure. - { - LOCK(cs); - unsigned char nVersion = 0; - READWRITE(nVersion); - READWRITE(nKey); - READWRITE(nNew); - READWRITE(nTried); - - CAddrMan *am = const_cast(this); - if (fWrite) - { - int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT; - READWRITE(nUBuckets); - std::map mapUnkIds; - int nIds = 0; - for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) - { - if (nIds == nNew) break; // this means nNew was wrong, oh ow - mapUnkIds[(*it).first] = nIds; - CAddrInfo &info = (*it).second; - if (info.nRefCount) - { - READWRITE(info); - nIds++; - } - } - nIds = 0; - for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) - { - if (nIds == nTried) break; // this means nTried was wrong, oh ow - CAddrInfo &info = (*it).second; - if (info.fInTried) - { - READWRITE(info); - nIds++; - } - } - for (std::vector >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++) - { - const std::set &vNew = (*it); - int nSize = vNew.size(); - READWRITE(nSize); - for (std::set::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) - { - int nIndex = mapUnkIds[*it2]; - READWRITE(nIndex); - } - } - } else { - int nUBuckets = 0; - READWRITE(nUBuckets); - am->nIdCount = 0; - am->mapInfo.clear(); - am->mapAddr.clear(); - am->vRandom.clear(); - am->vvTried = std::vector >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector(0)); - am->vvNew = std::vector >(ADDRMAN_NEW_BUCKET_COUNT, std::set()); - for (int n = 0; n < am->nNew; n++) - { - CAddrInfo &info = am->mapInfo[n]; - READWRITE(info); - am->mapAddr[info] = n; - info.nRandomPos = vRandom.size(); - am->vRandom.push_back(n); - if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) - { - am->vvNew[info.GetNewBucket(am->nKey)].insert(n); - info.nRefCount++; - } - } - am->nIdCount = am->nNew; - int nLost = 0; - for (int n = 0; n < am->nTried; n++) - { - CAddrInfo info; - READWRITE(info); - std::vector &vTried = am->vvTried[info.GetTriedBucket(am->nKey)]; - if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) - { - info.nRandomPos = vRandom.size(); - info.fInTried = true; - am->vRandom.push_back(am->nIdCount); - am->mapInfo[am->nIdCount] = info; - am->mapAddr[info] = am->nIdCount; - vTried.push_back(am->nIdCount); - am->nIdCount++; - } else { - nLost++; - } - } - am->nTried -= nLost; - for (int b = 0; b < nUBuckets; b++) - { - std::set &vNew = am->vvNew[b]; - int nSize = 0; - READWRITE(nSize); - for (int n = 0; n < nSize; n++) - { - int nIndex = 0; - READWRITE(nIndex); - CAddrInfo &info = am->mapInfo[nIndex]; - if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) - { - info.nRefCount++; - vNew.insert(nIndex); - } - } - } - } - } - });) - - CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set()) - { - nKey.resize(32); - RAND_bytes(&nKey[0], 32); - - nIdCount = 0; - nTried = 0; - nNew = 0; - } - - // Return the number of (unique) addresses in all tables. - int size() - { - return vRandom.size(); - } - - // Consistency check - void Check() - { -#ifdef DEBUG_ADDRMAN - { - LOCK(cs); - int err; - if ((err=Check_())) - printf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err); - } -#endif - } - - // Add a single address. - bool Add(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty = 0) - { - bool fRet = false; - { - LOCK(cs); - Check(); - fRet |= Add_(addr, source, nTimePenalty); - Check(); - } - if (fRet) - printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); - return fRet; - } - - // Add multiple addresses. - bool Add(const std::vector &vAddr, const CNetAddr& source, int64 nTimePenalty = 0) - { - 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(); - } - if (nAdd) - printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); - return nAdd > 0; - } - - // Mark an entry as accessible. - void Good(const CService &addr, int64 nTime = GetAdjustedTime()) - { - { - LOCK(cs); - Check(); - Good_(addr, nTime); - Check(); - } - } - - // Mark an entry as connection attempted to. - void Attempt(const CService &addr, int64 nTime = GetAdjustedTime()) - { - { - LOCK(cs); - Check(); - Attempt_(addr, nTime); - Check(); - } - } - - // Choose an address to connect to. - // nUnkBias determines how much "new" entries are favored over "tried" ones (0-100). - CAddress Select(int nUnkBias = 50) - { - CAddress addrRet; - { - LOCK(cs); - Check(); - addrRet = Select_(nUnkBias); - Check(); - } - return addrRet; - } - - // Return a bunch of addresses, selected at random. - std::vector GetAddr() - { - Check(); - std::vector vAddr; - { - LOCK(cs); - GetAddr_(vAddr); - } - Check(); - return vAddr; - } - - // Mark an entry as currently-connected-to. - void Connected(const CService &addr, int64 nTime = GetAdjustedTime()) - { - { - LOCK(cs); - Check(); - Connected_(addr, nTime); - Check(); - } - } -}; - -#endif diff --git a/src/alert.cpp b/src/alert.cpp deleted file mode 100644 index 42981709..00000000 --- a/src/alert.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// -// Alert system -// - -#include -#include -#include -#include -#include - -#include "alert.h" -#include "key.h" -#include "net.h" -#include "sync.h" -#include "ui_interface.h" - -using namespace std; - -map mapAlerts; -CCriticalSection cs_mapAlerts; - -static const char* pszMainKey = "04a0a5a88b2cebf0d6a7d20214e6bd51c903645505f72d05d536c9f3b996ec428150931cc6e9943f8603f756907b8793d715cb3b4fcca2735110c324c98158f456"; -static const char* pszTestKey = "0400a665daf19bf39db0d2072ea4edd5ca7a9070b81001934bfd4fa0d12561b105be22ea92c4608555f97819ffeb1c80cd701fcc43cd6a38ceefb128699e593aa3"; - -void CUnsignedAlert::SetNull() -{ - nVersion = 1; - nRelayUntil = 0; - nExpiration = 0; - nID = 0; - nCancel = 0; - setCancel.clear(); - nMinVer = 0; - nMaxVer = 0; - setSubVer.clear(); - nPriority = 0; - - strComment.clear(); - strStatusBar.clear(); - strReserved.clear(); -} - -std::string CUnsignedAlert::ToString() const -{ - std::string strSetCancel; - BOOST_FOREACH(int n, setCancel) - strSetCancel += strprintf("%d ", n); - std::string strSetSubVer; - BOOST_FOREACH(std::string str, setSubVer) - strSetSubVer += "\"" + str + "\" "; - return strprintf( - "CAlert(\n" - " nVersion = %d\n" - " nRelayUntil = %"PRI64d"\n" - " nExpiration = %"PRI64d"\n" - " nID = %d\n" - " nCancel = %d\n" - " setCancel = %s\n" - " nMinVer = %d\n" - " nMaxVer = %d\n" - " setSubVer = %s\n" - " nPriority = %d\n" - " strComment = \"%s\"\n" - " strStatusBar = \"%s\"\n" - ")\n", - nVersion, - nRelayUntil, - nExpiration, - nID, - nCancel, - strSetCancel.c_str(), - nMinVer, - nMaxVer, - strSetSubVer.c_str(), - nPriority, - strComment.c_str(), - strStatusBar.c_str()); -} - -void CUnsignedAlert::print() const -{ - printf("%s", ToString().c_str()); -} - -void CAlert::SetNull() -{ - CUnsignedAlert::SetNull(); - vchMsg.clear(); - vchSig.clear(); -} - -bool CAlert::IsNull() const -{ - return (nExpiration == 0); -} - -uint256 CAlert::GetHash() const -{ - return Hash(this->vchMsg.begin(), this->vchMsg.end()); -} - -bool CAlert::IsInEffect() const -{ - return (GetAdjustedTime() < nExpiration); -} - -bool CAlert::Cancels(const CAlert& alert) const -{ - if (!IsInEffect()) - return false; // this was a no-op before 31403 - return (alert.nID <= nCancel || setCancel.count(alert.nID)); -} - -bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const -{ - // TODO: rework for client-version-embedded-in-strSubVer ? - return (IsInEffect() && - nMinVer <= nVersion && nVersion <= nMaxVer && - (setSubVer.empty() || setSubVer.count(strSubVerIn))); -} - -bool CAlert::AppliesToMe() const -{ - return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); -} - -bool CAlert::RelayTo(CNode* pnode) const -{ - if (!IsInEffect()) - return false; - // returns true if wasn't already contained in the set - if (pnode->setKnown.insert(GetHash()).second) - { - if (AppliesTo(pnode->nVersion, pnode->strSubVer) || - AppliesToMe() || - GetAdjustedTime() < nRelayUntil) - { - pnode->PushMessage("alert", *this); - return true; - } - } - return false; -} - -bool CAlert::CheckSignature() const -{ - CKey key; - if (!key.SetPubKey(ParseHex(fTestNet ? pszTestKey : pszMainKey))) - return error("CAlert::CheckSignature() : SetPubKey failed"); - if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) - return error("CAlert::CheckSignature() : verify signature failed"); - - // Now unserialize the data - CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); - sMsg >> *(CUnsignedAlert*)this; - return true; -} - -CAlert CAlert::getAlertByHash(const uint256 &hash) -{ - CAlert retval; - { - LOCK(cs_mapAlerts); - map::iterator mi = mapAlerts.find(hash); - if(mi != mapAlerts.end()) - retval = mi->second; - } - return retval; -} - -bool CAlert::ProcessAlert(bool fThread) -{ - if (!CheckSignature()) - return false; - if (!IsInEffect()) - return false; - - // alert.nID=max is reserved for if the alert key is - // compromised. It must have a pre-defined message, - // must never expire, must apply to all versions, - // and must cancel all previous - // alerts or it will be ignored (so an attacker can't - // send an "everything is OK, don't panic" version that - // cannot be overridden): - int maxInt = std::numeric_limits::max(); - if (nID == maxInt) - { - if (!( - nExpiration == maxInt && - nCancel == (maxInt-1) && - nMinVer == 0 && - nMaxVer == maxInt && - setSubVer.empty() && - nPriority == maxInt && - strStatusBar == "URGENT: Alert key compromised, upgrade required" - )) - return false; - } - - { - LOCK(cs_mapAlerts); - // Cancel previous alerts - for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) - { - const CAlert& alert = (*mi).second; - if (Cancels(alert)) - { - printf("cancelling alert %d\n", alert.nID); - uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); - mapAlerts.erase(mi++); - } - else if (!alert.IsInEffect()) - { - printf("expiring alert %d\n", alert.nID); - uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); - mapAlerts.erase(mi++); - } - else - mi++; - } - - // Check if this alert has been cancelled - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - { - const CAlert& alert = item.second; - if (alert.Cancels(*this)) - { - printf("alert already cancelled by %d\n", alert.nID); - return false; - } - } - - // Add to mapAlerts - mapAlerts.insert(make_pair(GetHash(), *this)); - // Notify UI and -alertnotify if it applies to me - if(AppliesToMe()) - { - uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); - std::string strCmd = GetArg("-alertnotify", ""); - if (!strCmd.empty()) - { - // Alert text should be plain ascii coming from a trusted source, but to - // be safe we first strip anything not in safeChars, then add single quotes around - // the whole string before passing it to the shell: - std::string singleQuote("'"); - // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything - // even possibly remotely dangerous like & or > - std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); - std::string safeStatus; - for (std::string::size_type i = 0; i < strStatusBar.size(); i++) - { - if (safeChars.find(strStatusBar[i]) != std::string::npos) - safeStatus.push_back(strStatusBar[i]); - } - safeStatus = singleQuote+safeStatus+singleQuote; - boost::replace_all(strCmd, "%s", safeStatus); - - if (fThread) - boost::thread t(runCommand, strCmd); // thread runs free - else - runCommand(strCmd); - } - } - } - - printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); - return true; -} diff --git a/src/alert.h b/src/alert.h deleted file mode 100644 index 25e140f5..00000000 --- a/src/alert.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef _BITCOINALERT_H_ -#define _BITCOINALERT_H_ 1 - -#include -#include - -#include "uint256.h" -#include "util.h" - -class CNode; - -/** Alerts are for notifying old versions if they become too obsolete and - * need to upgrade. The message is displayed in the status bar. - * Alert messages are broadcast as a vector of signed data. Unserializing may - * not read the entire buffer if the alert is for a newer version, but older - * versions can still relay the original data. - */ -class CUnsignedAlert -{ -public: - int nVersion; - int64 nRelayUntil; // when newer nodes stop relaying to newer nodes - int64 nExpiration; - int nID; - int nCancel; - std::set setCancel; - int nMinVer; // lowest version inclusive - int nMaxVer; // highest version inclusive - std::set setSubVer; // empty matches all - int nPriority; - - // Actions - std::string strComment; - std::string strStatusBar; - std::string strReserved; - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(nRelayUntil); - READWRITE(nExpiration); - READWRITE(nID); - READWRITE(nCancel); - READWRITE(setCancel); - READWRITE(nMinVer); - READWRITE(nMaxVer); - READWRITE(setSubVer); - READWRITE(nPriority); - - READWRITE(strComment); - READWRITE(strStatusBar); - READWRITE(strReserved); - ) - - void SetNull(); - - std::string ToString() const; - void print() const; -}; - -/** An alert is a combination of a serialized CUnsignedAlert and a signature. */ -class CAlert : public CUnsignedAlert -{ -public: - std::vector vchMsg; - std::vector vchSig; - - CAlert() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(vchMsg); - READWRITE(vchSig); - ) - - void SetNull(); - bool IsNull() const; - uint256 GetHash() const; - bool IsInEffect() const; - bool Cancels(const CAlert& alert) const; - bool AppliesTo(int nVersion, std::string strSubVerIn) const; - bool AppliesToMe() const; - bool RelayTo(CNode* pnode) const; - bool CheckSignature() const; - bool ProcessAlert(bool fThread = true); - - /* - * Get copy of (active) alert object by hash. Returns a null alert if it is not found. - */ - static CAlert getAlertByHash(const uint256 &hash); -}; - -#endif diff --git a/src/bignum.h b/src/bignum.h index 0881807d..a7ecf729 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -390,26 +390,6 @@ public: return ToString(16); } - unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const - { - return ::GetSerializeSize(getvch(), nType, nVersion); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const - { - ::Serialize(s, getvch(), nType, nVersion); - } - - template - void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) - { - std::vector vch; - ::Unserialize(s, vch, nType, nVersion); - setvch(vch); - } - - bool operator!() const { return BN_is_zero(this); diff --git a/src/bitcoinminer.cpp b/src/bitcoinminer.cpp index 23aaea97..9b946a71 100644 --- a/src/bitcoinminer.cpp +++ b/src/bitcoinminer.cpp @@ -23,9 +23,6 @@ void BitcoinMiner(CBlockProvider *block_provider, unsigned int thread_id) SetThreadPriority(THREAD_PRIORITY_LOWEST); RenameThread("primecoin-miner"); - // Each thread has its own kcd ey and counter - unsigned int nExtraNonce = 0; //^ - unsigned int nPrimorialMultiplier = nPrimorialHashFactor; double dTimeExpected = 0; // time expected to prime chain (micro-second) int64 nSieveGenTime = 0; // how many milliseconds sieve generation took @@ -41,7 +38,6 @@ void BitcoinMiner(CBlockProvider *block_provider, unsigned int thread_id) // CBlockIndex* pindexPrev = pindexBest; - auto_ptr pblocktemplate; if ((pblock = block_provider->getBlock(thread_id, pblock == NULL ? 0 : pblock->nTime)) == NULL) { //server not reachable? MilliSleep(20000); continue; @@ -59,11 +55,6 @@ void BitcoinMiner(CBlockProvider *block_provider, unsigned int thread_id) old_nonce = 0; } - if (fDebug && GetBoolArg("-printmining")) - printf("Running PrimecoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), - ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); - - // // Search // int64 nStart = GetTime(); diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp deleted file mode 100644 index f6d5355f..00000000 --- a/src/bitcoinrpc.cpp +++ /dev/null @@ -1,1381 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 The Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#include "init.h" -#include "util.h" -#include "sync.h" -#include "ui_interface.h" -#include "base58.h" -#include "bitcoinrpc.h" -#include "db.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace json_spirit; - -// Key used by getwork/getblocktemplate miners. -// Allocated in StartRPCThreads, free'd in StopRPCThreads -CReserveKey* pMiningKey = NULL; - -static std::string strRPCUserColonPass; - -// These are created by StartRPCThreads, destroyed in StopRPCThreads -static asio::io_service* rpc_io_service = NULL; -static ssl::context* rpc_ssl_context = NULL; -static boost::thread_group* rpc_worker_group = NULL; - -static inline unsigned short GetDefaultRPCPort() -{ - return GetBoolArg("-testnet", false) ? TESTNET_RPC_PORT : RPC_PORT; -} - -Object JSONRPCError(int code, const string& message) -{ - Object error; - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; -} - -void RPCTypeCheck(const Array& params, - const list& typesExpected, - bool fAllowNull) -{ - unsigned int i = 0; - BOOST_FOREACH(Value_type t, typesExpected) - { - if (params.size() <= i) - break; - - const Value& v = params[i]; - if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) - { - string err = strprintf("Expected type %s, got %s", - Value_type_name[t], Value_type_name[v.type()]); - throw JSONRPCError(RPC_TYPE_ERROR, err); - } - i++; - } -} - -void RPCTypeCheck(const Object& o, - const map& typesExpected, - bool fAllowNull) -{ - BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) - { - const Value& v = find_value(o, t.first); - if (!fAllowNull && v.type() == null_type) - throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str())); - - if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) - { - string err = strprintf("Expected type %s for %s, got %s", - Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]); - throw JSONRPCError(RPC_TYPE_ERROR, err); - } - } -} - -int64 AmountFromValue(const Value& value) -{ - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - return nAmount; -} - -Value ValueFromAmount(int64 amount) -{ - return (double)amount / (double)COIN; -} - -std::string HexBits(unsigned int nBits) -{ - union { - int32_t nBits; - char cBits[4]; - } uBits; - uBits.nBits = htonl((int32_t)nBits); - return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); -} - - - -/// -/// Note: This interface may still be subject to change. -/// - -string CRPCTable::help(string strCommand) const -{ - string strRet; - set setDone; - for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) - { - const CRPCCommand *pcmd = mi->second; - string strMethod = mi->first; - // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod.find("label") != string::npos) - continue; - if (strCommand != "" && strMethod != strCommand) - continue; - try - { - Array params; - rpcfn_type pfn = pcmd->actor; - if (setDone.insert(pfn).second) - (*pfn)(params, true); - } - catch (std::exception& e) - { - // Help text is returned in an exception - string strHelp = string(e.what()); - if (strCommand == "") - if (strHelp.find('\n') != string::npos) - strHelp = strHelp.substr(0, strHelp.find('\n')); - strRet += strHelp + "\n"; - } - } - if (strRet == "") - strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); - strRet = strRet.substr(0,strRet.size()-1); - return strRet; -} - -Value help(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "help [command]\n" - "List commands, or get help for a command."); - - string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); - - return tableRPC.help(strCommand); -} - - -Value stop(const Array& params, bool fHelp) -{ - // Accept the deprecated and ignored 'detach' boolean argument - if (fHelp || params.size() > 1) - throw runtime_error( - "stop\n" - "Stop Primecoin server."); - // Shutdown will take long enough that the response should get back - StartShutdown(); - return "Primecoin server stopping"; -} - - - -// -// Call Table -// - - -static const CRPCCommand vRPCCommands[] = -{ // name actor (function) okSafeMode threadSafe - // ------------------------ ----------------------- ---------- ---------- - { "help", &help, true, true }, - { "stop", &stop, true, true }, - { "getblockcount", &getblockcount, true, false }, - { "getconnectioncount", &getconnectioncount, true, false }, - { "getpeerinfo", &getpeerinfo, true, false }, - { "addnode", &addnode, true, true }, - { "getaddednodeinfo", &getaddednodeinfo, true, true }, - { "getdifficulty", &getdifficulty, true, false }, - { "getgenerate", &getgenerate, true, false }, - { "setgenerate", &setgenerate, true, false }, - { "getsievepercentage", &getsievepercentage, true, false }, - { "setsievepercentage", &setsievepercentage, true, false }, - { "getsieveextensions", &getsieveextensions, true, false }, - { "setsieveextensions", &setsieveextensions, true, false }, - { "getprimespersec", &getprimespersec, true, false }, - { "getchainspermin", &getchainspermin, true, false }, - { "getinfo", &getinfo, true, false }, - { "getmininginfo", &getmininginfo, true, false }, - { "getnewaddress", &getnewaddress, true, false }, - { "getaccountaddress", &getaccountaddress, true, false }, - { "setaccount", &setaccount, true, false }, - { "getaccount", &getaccount, false, false }, - { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, - { "sendtoaddress", &sendtoaddress, false, false }, - { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, - { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, - { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, - { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, - { "backupwallet", &backupwallet, true, false }, - { "keypoolrefill", &keypoolrefill, true, false }, - { "walletpassphrase", &walletpassphrase, true, false }, - { "walletpassphrasechange", &walletpassphrasechange, false, false }, - { "walletlock", &walletlock, true, false }, - { "encryptwallet", &encryptwallet, false, false }, - { "validateaddress", &validateaddress, true, false }, - { "getbalance", &getbalance, false, false }, - { "move", &movecmd, false, false }, - { "sendfrom", &sendfrom, false, false }, - { "sendmany", &sendmany, false, false }, - { "addmultisigaddress", &addmultisigaddress, false, false }, - { "createmultisig", &createmultisig, true, true }, - { "getrawmempool", &getrawmempool, true, false }, - { "getblock", &getblock, false, false }, - { "getblockhash", &getblockhash, false, false }, - { "gettransaction", &gettransaction, false, false }, - { "listtransactions", &listtransactions, false, false }, - { "listaddressgroupings", &listaddressgroupings, false, false }, - { "signmessage", &signmessage, false, false }, - { "verifymessage", &verifymessage, false, false }, - { "getwork", &getwork, true, false }, - { "listaccounts", &listaccounts, false, false }, - { "settxfee", &settxfee, false, false }, - { "getblocktemplate", &getblocktemplate, true, false }, - { "submitblock", &submitblock, false, false }, - { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, true, false }, - { "importprivkey", &importprivkey, false, false }, - { "getcheckpoint", &getcheckpoint, true, false }, - { "sendcheckpoint", &sendcheckpoint, true, false }, - { "enforcecheckpoint", &enforcecheckpoint, true, false }, - { "makekeypair", &makekeypair, true, false }, - { "sendalert", &sendalert, true, false }, - { "listunspent", &listunspent, false, false }, - { "getrawtransaction", &getrawtransaction, false, false }, - { "createrawtransaction", &createrawtransaction, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false }, - { "signrawtransaction", &signrawtransaction, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false }, - { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, - { "gettxout", &gettxout, true, false }, - { "lockunspent", &lockunspent, false, false }, - { "listlockunspent", &listlockunspent, false, false }, - { "listprimerecords", &listprimerecords, false, false }, - { "listtopprimes", &listtopprimes, false, false }, -}; - -CRPCTable::CRPCTable() -{ - unsigned int vcidx; - for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) - { - const CRPCCommand *pcmd; - - pcmd = &vRPCCommands[vcidx]; - mapCommands[pcmd->name] = pcmd; - } -} - -const CRPCCommand *CRPCTable::operator[](string name) const -{ - map::const_iterator it = mapCommands.find(name); - if (it == mapCommands.end()) - return NULL; - return (*it).second; -} - -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -string HTTPPost(const string& strMsg, const map& mapRequestHeaders) -{ - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: primecoin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Connection: close\r\n" - << "Accept: application/json\r\n"; - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; - - return s.str(); -} - -string rfc1123Time() -{ - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); -} - -static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) -{ - if (nStatus == HTTP_UNAUTHORIZED) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: primecoin-json-rpc/%s\r\n" - "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 296\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "Error\r\n" - "\r\n" - "\r\n" - "

401 Unauthorized.

\r\n" - "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - const char *cStatus; - if (nStatus == HTTP_OK) cStatus = "OK"; - else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; - else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; - else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; - else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; - else cStatus = ""; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: %s\r\n" - "Content-Length: %"PRIszu"\r\n" - "Content-Type: application/json\r\n" - "Server: primecoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - cStatus, - rfc1123Time().c_str(), - keepalive ? "keep-alive" : "close", - strMsg.size(), - FormatFullVersion().c_str(), - strMsg.c_str()); -} - -bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, - string& http_method, string& http_uri) -{ - string str; - getline(stream, str); - - // HTTP request line is space-delimited - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return false; - - // HTTP methods permitted: GET, POST - http_method = vWords[0]; - if (http_method != "GET" && http_method != "POST") - return false; - - // HTTP URI must be an absolute path, relative to current host - http_uri = vWords[1]; - if (http_uri.size() == 0 || http_uri[0] != '/') - return false; - - // parse proto, if present - string strProto = ""; - if (vWords.size() > 2) - strProto = vWords[2]; - - proto = 0; - const char *ver = strstr(strProto.c_str(), "HTTP/1."); - if (ver != NULL) - proto = atoi(ver+7); - - return true; -} - -int ReadHTTPStatus(std::basic_istream& stream, int &proto) -{ - string str; - getline(stream, str); - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return HTTP_INTERNAL_SERVER_ERROR; - proto = 0; - const char *ver = strstr(str.c_str(), "HTTP/1."); - if (ver != NULL) - proto = atoi(ver+7); - return atoi(vWords[1].c_str()); -} - -int ReadHTTPHeaders(std::basic_istream& stream, map& mapHeadersRet) -{ - int nLen = 0; - loop - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; -} - -int ReadHTTPMessage(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet, - int nProto) -{ - mapHeadersRet.clear(); - strMessageRet = ""; - - // Read header - int nLen = ReadHTTPHeaders(stream, mapHeadersRet); - if (nLen < 0 || nLen > (int)MAX_SIZE) - return HTTP_INTERNAL_SERVER_ERROR; - - // Read message - if (nLen > 0) - { - vector vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } - - string sConHdr = mapHeadersRet["connection"]; - - if ((sConHdr != "close") && (sConHdr != "keep-alive")) - { - if (nProto >= 1) - mapHeadersRet["connection"] = "keep-alive"; - else - mapHeadersRet["connection"] = "close"; - } - - return HTTP_OK; -} - -bool HTTPAuthorized(map& mapHeaders) -{ - string strAuth = mapHeaders["authorization"]; - if (strAuth.substr(0,6) != "Basic ") - return false; - string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); - string strUserPass = DecodeBase64(strUserPass64); - return strUserPass == strRPCUserColonPass; -} - -// -// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, -// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were -// unspecified (HTTP errors and contents of 'error'). -// -// 1.0 spec: http://json-rpc.org/wiki/specification -// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http -// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx -// - -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) -{ - Object request; - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; -} - -Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) -{ - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return reply; -} - -string JSONRPCReply(const Value& result, const Value& error, const Value& id) -{ - Object reply = JSONRPCReplyObj(result, error, id); - return write_string(Value(reply), false) + "\n"; -} - -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) -{ - // Send error reply from json-rpc error object - int nStatus = HTTP_INTERNAL_SERVER_ERROR; - int code = find_value(objError, "code").get_int(); - if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; - else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply, false) << std::flush; -} - -bool ClientAllowed(const boost::asio::ip::address& address) -{ - // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses - if (address.is_v6() - && (address.to_v6().is_v4_compatible() - || address.to_v6().is_v4_mapped())) - return ClientAllowed(address.to_v6().to_v4()); - - if (address == asio::ip::address_v4::loopback() - || address == asio::ip::address_v6::loopback() - || (address.is_v4() - // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet) - && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000)) - return true; - - const string strAddress = address.to_string(); - const vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH(string strAllow, vAllow) - if (WildcardMatch(strAddress, strAllow)) - return true; - return false; -} - -// -// IOStream device that speaks SSL but can also speak non-SSL -// -template -class SSLIOStreamDevice : public iostreams::device { -public: - SSLIOStreamDevice(asio::ssl::stream &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(asio::buffer(s, n)); - return stream.next_layer().read_some(asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); - return asio::write(stream.next_layer(), asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - ip::tcp::resolver resolver(stream.get_io_service()); - ip::tcp::resolver::query query(server.c_str(), port.c_str()); - ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - ip::tcp::resolver::iterator end; - boost::system::error_code error = asio::error::host_not_found; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - asio::ssl::stream& stream; -}; - -class AcceptedConnection -{ -public: - virtual ~AcceptedConnection() {} - - virtual std::iostream& stream() = 0; - virtual std::string peer_address_to_string() const = 0; - virtual void close() = 0; -}; - -template -class AcceptedConnectionImpl : public AcceptedConnection -{ -public: - AcceptedConnectionImpl( - asio::io_service& io_service, - ssl::context &context, - bool fUseSSL) : - sslStream(io_service, context), - _d(sslStream, fUseSSL), - _stream(_d) - { - } - - virtual std::iostream& stream() - { - return _stream; - } - - virtual std::string peer_address_to_string() const - { - return peer.address().to_string(); - } - - virtual void close() - { - _stream.close(); - } - - typename Protocol::endpoint peer; - asio::ssl::stream sslStream; - -private: - SSLIOStreamDevice _d; - iostreams::stream< SSLIOStreamDevice > _stream; -}; - -void ServiceConnection(AcceptedConnection *conn); - -// Forward declaration required for RPCListen -template -static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, - ssl::context& context, - bool fUseSSL, - AcceptedConnection* conn, - const boost::system::error_code& error); - -/** - * Sets up I/O resources to accept and handle a new connection. - */ -template -static void RPCListen(boost::shared_ptr< basic_socket_acceptor > acceptor, - ssl::context& context, - const bool fUseSSL) -{ - // Accept connection - AcceptedConnectionImpl* conn = new AcceptedConnectionImpl(acceptor->get_io_service(), context, fUseSSL); - - acceptor->async_accept( - conn->sslStream.lowest_layer(), - conn->peer, - boost::bind(&RPCAcceptHandler, - acceptor, - boost::ref(context), - fUseSSL, - conn, - boost::asio::placeholders::error)); -} - -/** - * Accept and handle incoming connection. - */ -template -static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, - ssl::context& context, - const bool fUseSSL, - AcceptedConnection* conn, - const boost::system::error_code& error) -{ - // Immediately start accepting new connections, except when we're cancelled or our socket is closed. - if (error != asio::error::operation_aborted && acceptor->is_open()) - RPCListen(acceptor, context, fUseSSL); - - AcceptedConnectionImpl* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); - - // TODO: Actually handle errors - if (error) - { - delete conn; - } - - // Restrict callers by IP. It is important to - // do this before starting client thread, to filter out - // certain DoS and misbehaving clients. - else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address())) - { - // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. - if (!fUseSSL) - conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; - delete conn; - } - else { - ServiceConnection(conn); - conn->close(); - delete conn; - } -} - -void StartRPCThreads() -{ - // getwork/getblocktemplate mining rewards paid here: - pMiningKey = new CReserveKey(pwalletMain); - - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if ((mapArgs["-rpcpassword"] == "") || - (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) - { - unsigned char rand_pwd[32]; - RAND_bytes(rand_pwd, 32); - string strWhatAmI = "To use primecoind"; - if (mapArgs.count("-server")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); - else if (mapArgs.count("-daemon")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); - uiInterface.ThreadSafeMessageBox(strprintf( - _("%s, you must set a rpcpassword in the configuration file:\n" - "%s\n" - "It is recommended you use the following random password:\n" - "rpcuser=primecoinrpc\n" - "rpcpassword=%s\n" - "(you do not need to remember this password)\n" - "The username and password MUST NOT be the same.\n" - "If the file does not exist, create it with owner-readable-only file permissions.\n" - "It is also recommended to set alertnotify so you are notified of problems;\n" - "for example: alertnotify=echo %%s | mail -s \"Primecoin Alert\" admin@foo.com\n"), - strWhatAmI.c_str(), - GetConfigFile().string().c_str(), - EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return; - } - - assert(rpc_io_service == NULL); - rpc_io_service = new asio::io_service(); - rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); - - const bool fUseSSL = GetBoolArg("-rpcssl"); - - if (fUseSSL) - { - rpc_ssl_context->set_options(ssl::context::no_sslv2); - - filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); - if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; - if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); - - filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); - if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; - if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); - - string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); - } - - // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets - const bool loopback = !mapArgs.count("-rpcallowip"); - asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); - boost::system::error_code v6_only_error; - boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); - - bool fListening = false; - std::string strerr; - try - { - acceptor->open(endpoint.protocol()); - acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - - // Try making the socket dual IPv6/IPv4 (if listening on the "any" address) - acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error); - - acceptor->bind(endpoint); - acceptor->listen(socket_base::max_connections); - - RPCListen(acceptor, *rpc_ssl_context, fUseSSL); - - fListening = true; - } - catch(boost::system::system_error &e) - { - strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); - } - - try { - // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately - if (!fListening || loopback || v6_only_error) - { - bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); - endpoint.address(bindAddress); - - acceptor.reset(new ip::tcp::acceptor(*rpc_io_service)); - acceptor->open(endpoint.protocol()); - acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - acceptor->bind(endpoint); - acceptor->listen(socket_base::max_connections); - - RPCListen(acceptor, *rpc_ssl_context, fUseSSL); - - fListening = true; - } - } - catch(boost::system::system_error &e) - { - strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what()); - } - - if (!fListening) { - uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return; - } - - rpc_worker_group = new boost::thread_group(); - for (int i = 0; i < GetArg("-rpcthreads", 4); i++) - rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); -} - -void StopRPCThreads() -{ - delete pMiningKey; pMiningKey = NULL; - - if (rpc_io_service == NULL) return; - - rpc_io_service->stop(); - rpc_worker_group->join_all(); - delete rpc_worker_group; rpc_worker_group = NULL; - delete rpc_ssl_context; rpc_ssl_context = NULL; - delete rpc_io_service; rpc_io_service = NULL; -} - -class JSONRequest -{ -public: - Value id; - string strMethod; - Array params; - - JSONRequest() { id = Value::null; } - void parse(const Value& valRequest); -}; - -void JSONRequest::parse(const Value& valRequest) -{ - // Parse request - if (valRequest.type() != obj_type) - throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); - const Object& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) - throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); - if (valMethod.type() != str_type) - throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); - strMethod = valMethod.get_str(); - if (strMethod != "getwork" && strMethod != "getblocktemplate") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - - // Parse params - Value valParams = find_value(request, "params"); - if (valParams.type() == array_type) - params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); - else - throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); -} - -static Object JSONRPCExecOne(const Value& req) -{ - Object rpc_result; - - JSONRequest jreq; - try { - jreq.parse(req); - - Value result = tableRPC.execute(jreq.strMethod, jreq.params); - rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); - } - catch (Object& objError) - { - rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); - } - catch (std::exception& e) - { - rpc_result = JSONRPCReplyObj(Value::null, - JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); - } - - return rpc_result; -} - -static string JSONRPCExecBatch(const Array& vReq) -{ - Array ret; - for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) - ret.push_back(JSONRPCExecOne(vReq[reqIdx])); - - return write_string(Value(ret), false) + "\n"; -} - -void ServiceConnection(AcceptedConnection *conn) -{ - bool fRun = true; - while (fRun) - { - int nProto = 0; - map mapHeaders; - string strRequest, strMethod, strURI; - - // Read HTTP request line - if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) - break; - - // Read HTTP message headers and body - ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); - - if (strURI != "/") { - conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; - break; - } - - // Check authorization - if (mapHeaders.count("authorization") == 0) - { - conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; - break; - } - if (!HTTPAuthorized(mapHeaders)) - { - printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); - /* Deter brute-forcing short passwords. - If this results in a DOS the user really - shouldn't have their RPC port exposed.*/ - if (mapArgs["-rpcpassword"].size() < 20) - MilliSleep(250); - - conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; - break; - } - if (mapHeaders["connection"] == "close") - fRun = false; - - JSONRequest jreq; - try - { - // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest)) - throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); - - string strReply; - - // singleton request - if (valRequest.type() == obj_type) { - jreq.parse(valRequest); - - Value result = tableRPC.execute(jreq.strMethod, jreq.params); - - // Send reply - strReply = JSONRPCReply(result, Value::null, jreq.id); - - // array of requests - } else if (valRequest.type() == array_type) - strReply = JSONRPCExecBatch(valRequest.get_array()); - else - throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); - - conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; - } - catch (Object& objError) - { - ErrorReply(conn->stream(), objError, jreq.id); - break; - } - catch (std::exception& e) - { - ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); - break; - } - } -} - -json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const -{ - // Find method - const CRPCCommand *pcmd = tableRPC[strMethod]; - if (!pcmd) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); - - // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && - !pcmd->okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); - - try - { - // Execute - Value result; - { - if (pcmd->threadSafe) - result = pcmd->actor(params, false); - else { - LOCK2(cs_main, pwalletMain->cs_wallet); - result = pcmd->actor(params, false); - } - } - return result; - } - catch (std::exception& e) - { - throw JSONRPCError(RPC_MISC_ERROR, e.what()); - } -} - -Object CallRPC(const string& strMethod, const Array& params, const std::string& server, const std::string& port, map& mapHeadersRet) -{ - if (mapArgs["-pooluser"] == "" || mapArgs["-poolpassword"] == "") - throw runtime_error("Add the parameter -pooluser= and -poolpassword=\n"); - - Object empty; - // Connect to server:port - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - asio::ssl::stream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice > stream(d); - if (!d.connect(server, port)) { - printf("couldn't connect to server\n"); - return empty; - } - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-pooluser"] + ":" + mapArgs["-poolpassword"]); //TODO: this is not thread-safe, right?! - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive HTTP reply status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - - // Receive HTTP reply message headers and body - string strReply; - ReadHTTPMessage(stream, mapHeadersRet, strReply, nProto); - - if (nStatus == HTTP_UNAUTHORIZED) { - printf("incorrect pooluser or poolpassword (authorization failed)\n"); - return empty; - } - else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) { - printf("server returned HTTP error %d\n", nStatus); - return empty; - } - else if (strReply.empty()) { - printf("no response from server\n"); - return empty; - } - - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) { - printf("couldn't parse reply from server\n"); - return empty; - } - const Object& reply = valReply.get_obj(); - if (reply.empty()) { - printf("expected reply to have result, error and id properties\n"); - return empty; - } - - return reply; -} - -Object CallRPC(const string& strMethod, const Array& params) -{ - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword= in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().string().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - asio::ssl::stream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice > stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort())))) - throw runtime_error("couldn't connect to server"); - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive HTTP reply status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - - // Receive HTTP reply message headers and body - map mapHeaders; - string strReply; - ReadHTTPMessage(stream, mapHeaders, strReply, nProto); - - if (nStatus == HTTP_UNAUTHORIZED) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); - - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - - - - -template -void ConvertTo(Value& value, bool fAllowNull=false) -{ - if (fAllowNull && value.type() == null_type) - return; - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - string strJSON = value.get_str(); - if (!read_string(strJSON, value2)) - throw runtime_error(string("Error parsing JSON:")+strJSON); - ConvertTo(value2, fAllowNull); - value = value2; - } - else - { - value = value.get_value(); - } -} - -// Convert strings to command-specific RPC representation -Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams) -{ - Array params; - BOOST_FOREACH(const std::string ¶m, strParams) - params.push_back(param); - - int n = params.size(); - - // - // Special case non-string parameter types - // - if (strMethod == "stop" && n > 0) ConvertTo(params[0]); - if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); - if (strMethod == "setsievepercentage" && n > 0) ConvertTo(params[0]); - if (strMethod == "setsieveextensions" && n > 0) ConvertTo(params[0]); - if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); - if (strMethod == "move" && n > 2) ConvertTo(params[2]); - if (strMethod == "move" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); - if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); - if (strMethod == "getblocktemplate" && n > 0) ConvertTo(params[0]); - if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); - if (strMethod == "sendalert" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendalert" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendalert" && n > 4) ConvertTo(params[4]); - if (strMethod == "sendalert" && n > 5) ConvertTo(params[5]); - if (strMethod == "sendalert" && n > 6) ConvertTo(params[6]); - if (strMethod == "enforcecheckpoint" && n > 0) ConvertTo(params[0]); - if (strMethod == "sendmany" && n > 1) ConvertTo(params[1]); - if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); - if (strMethod == "addmultisigaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "createmultisig" && n > 0) ConvertTo(params[0]); - if (strMethod == "createmultisig" && n > 1) ConvertTo(params[1]); - if (strMethod == "listunspent" && n > 0) ConvertTo(params[0]); - if (strMethod == "listunspent" && n > 1) ConvertTo(params[1]); - if (strMethod == "listunspent" && n > 2) ConvertTo(params[2]); - if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); - if (strMethod == "createrawtransaction" && n > 0) ConvertTo(params[0]); - if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); - if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); - if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); - if (strMethod == "gettxout" && n > 1) ConvertTo(params[1]); - if (strMethod == "gettxout" && n > 2) ConvertTo(params[2]); - if (strMethod == "lockunspent" && n > 0) ConvertTo(params[0]); - if (strMethod == "lockunspent" && n > 1) ConvertTo(params[1]); - if (strMethod == "importprivkey" && n > 2) ConvertTo(params[2]); - if (strMethod == "listprimerecords" && n > 0) ConvertTo(params[0]); - if (strMethod == "listtopprimes" && n > 0) ConvertTo(params[0]); - - return params; -} - -int CommandLineRPC(int argc, char *argv[]) -{ - string strPrint; - int nRet = 0; - try - { - // Skip switches - while (argc > 1 && IsSwitchChar(argv[1][0])) - { - argc--; - argv++; - } - - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - std::vector strParams(&argv[2], &argv[argc]); - Array params = RPCConvertValues(strMethod, strParams); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } - } - catch (boost::thread_interrupted) { - throw; - } - catch (std::exception& e) { - strPrint = string("error: ") + e.what(); - nRet = 87; - } - catch (...) { - PrintException(NULL, "CommandLineRPC()"); - } - - if (strPrint != "") - { - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); - } - return nRet; -} - - - - -#ifdef TEST -int main(int argc, char *argv[]) -{ -#ifdef _MSC_VER - // Turn off Microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - try - { - if (argc >= 2 && string(argv[1]) == "-server") - { - printf("server ready\n"); - ThreadRPCServer(NULL); - } - else - { - return CommandLineRPC(argc, argv); - } - } - catch (boost::thread_interrupted) { - throw; - } - catch (std::exception& e) { - PrintException(&e, "main()"); - } catch (...) { - PrintException(NULL, "main()"); - } - return 0; -} -#endif - -const CRPCTable tableRPC; diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h deleted file mode 100644 index 73a68a4d..00000000 --- a/src/bitcoinrpc.h +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#ifndef _BITCOINRPC_H_ -#define _BITCOINRPC_H_ 1 - -#include -#include -#include - -class CBlockIndex; -class CReserveKey; - -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" -#include "json/json_spirit_utils.h" - -#include "util.h" - -// HTTP status codes -enum HTTPStatusCode -{ - HTTP_OK = 200, - HTTP_BAD_REQUEST = 400, - HTTP_UNAUTHORIZED = 401, - HTTP_FORBIDDEN = 403, - HTTP_NOT_FOUND = 404, - HTTP_INTERNAL_SERVER_ERROR = 500, -}; - -// Bitcoin RPC error codes -enum RPCErrorCode -{ - // Standard JSON-RPC 2.0 errors - RPC_INVALID_REQUEST = -32600, - RPC_METHOD_NOT_FOUND = -32601, - RPC_INVALID_PARAMS = -32602, - RPC_INTERNAL_ERROR = -32603, - RPC_PARSE_ERROR = -32700, - - // General application defined errors - RPC_MISC_ERROR = -1, // std::exception thrown in command handling - RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode - RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter - RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key - RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation - RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter - RPC_DATABASE_ERROR = -20, // Database error - RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format - - // P2P client errors - RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected - RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks - - // Wallet errors - RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) - RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account - RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name - RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first - RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first - RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect - RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) - RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet - RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked -}; - -json_spirit::Object JSONRPCError(int code, const std::string& message); - -void StartRPCThreads(); -void StopRPCThreads(); -int CommandLineRPC(int argc, char *argv[]); - -/** Convert parameter values for RPC call from strings to command-specific JSON objects. */ -json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams); - -/* - Type-check arguments; throws JSONRPCError if wrong type given. Does not check that - the right number of arguments are passed, just that any passed are the correct type. - Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); -*/ -void RPCTypeCheck(const json_spirit::Array& params, - const std::list& typesExpected, bool fAllowNull=false); -/* - Check for expected keys/value types in an Object. - Use like: RPCTypeCheck(object, boost::assign::map_list_of("name", str_type)("value", int_type)); -*/ -void RPCTypeCheck(const json_spirit::Object& o, - const std::map& typesExpected, bool fAllowNull=false); - -typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); - -class CRPCCommand -{ -public: - std::string name; - rpcfn_type actor; - bool okSafeMode; - bool threadSafe; -}; - -/** - * Bitcoin RPC command dispatcher. - */ -class CRPCTable -{ -private: - std::map mapCommands; -public: - CRPCTable(); - const CRPCCommand* operator[](std::string name) const; - std::string help(std::string name) const; - - /** - * Execute a method. - * @param method Method to execute - * @param params Array of arguments (JSON objects) - * @returns Result of the call. - * @throws an exception (json_spirit::Value) when an error happens. - */ - json_spirit::Value execute(const std::string &method, const json_spirit::Array ¶ms) const; -}; - -extern const CRPCTable tableRPC; -extern CReserveKey* pMiningKey; - -extern int64 nWalletUnlockTime; -extern int64 AmountFromValue(const json_spirit::Value& value); -extern json_spirit::Value ValueFromAmount(int64 amount); -extern double GetDifficulty(const CBlockIndex* blockindex = NULL); -extern double GetHashDifficulty(unsigned int nProofOfWorkType, const CBlockIndex* blockindex); -extern std::string HexBits(unsigned int nBits); -extern std::string HelpRequiringPassphrase(); -extern void EnsureWalletIsUnlocked(); - -extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp -extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getcheckpoint(const json_spirit::Array& params, bool fHelp); // in checkpointsync.cpp -extern json_spirit::Value sendcheckpoint(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value enforcecheckpoint(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp -extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp -extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getsievepercentage(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value setsievepercentage(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getsieveextensions(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value setsieveextensions(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getprimespersec(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getchainspermin(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp -extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value setaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getaddressesbyaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendtoaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value signmessage(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getreceivedbyaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getreceivedbyaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getbalance(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value createmultisig(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listaddressgroupings(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listaccounts(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listsinceblock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value backupwallet(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value keypoolrefill(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value walletpassphrase(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value walletpassphrasechange(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value walletlock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value encryptwallet(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value validateaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp -extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp -extern json_spirit::Value getdifficulty(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listprimerecords(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listtopprimes(const json_spirit::Array& params, bool fHelp); - -#endif diff --git a/src/bloom.cpp b/src/bloom.cpp deleted file mode 100644 index d9ec2efa..00000000 --- a/src/bloom.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include -#include - -#include "bloom.h" -#include "main.h" -#include "script.h" - -#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 -#define LN2 0.6931471805599453094172321214581765680755001343602552 - -using namespace std; - -static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; - -CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : -// The ideal size for a bloom filter with a given number of elements and false positive rate is: -// - nElements * log(fp rate) / ln(2)^2 -// We ignore filter parameters which will create a bloom filter larger than the protocol limits -vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), -// The ideal number of hash functions is filter size * ln(2) / number of elements -// Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits -// See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas -nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), -nTweak(nTweakIn), -nFlags(nFlagsIn) -{ -} - -inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector& vDataToHash) const -{ - // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values. - return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); -} - -void CBloomFilter::insert(const vector& vKey) -{ - if (vData.size() == 1 && vData[0] == 0xff) - return; - for (unsigned int i = 0; i < nHashFuncs; i++) - { - unsigned int nIndex = Hash(i, vKey); - // Sets bit nIndex of vData - vData[nIndex >> 3] |= bit_mask[7 & nIndex]; - } -} - -void CBloomFilter::insert(const COutPoint& outpoint) -{ - CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); - stream << outpoint; - vector data(stream.begin(), stream.end()); - insert(data); -} - -void CBloomFilter::insert(const uint256& hash) -{ - vector data(hash.begin(), hash.end()); - insert(data); -} - -bool CBloomFilter::contains(const vector& vKey) const -{ - if (vData.size() == 1 && vData[0] == 0xff) - return true; - for (unsigned int i = 0; i < nHashFuncs; i++) - { - unsigned int nIndex = Hash(i, vKey); - // Checks bit nIndex of vData - if (!(vData[nIndex >> 3] & bit_mask[7 & nIndex])) - return false; - } - return true; -} - -bool CBloomFilter::contains(const COutPoint& outpoint) const -{ - CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); - stream << outpoint; - vector data(stream.begin(), stream.end()); - return contains(data); -} - -bool CBloomFilter::contains(const uint256& hash) const -{ - vector data(hash.begin(), hash.end()); - return contains(data); -} - -bool CBloomFilter::IsWithinSizeConstraints() const -{ - return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; -} - -bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash) -{ - bool fFound = false; - // Match if the filter contains the hash of tx - // for finding tx when they appear in a block - if (contains(hash)) - fFound = true; - - for (unsigned int i = 0; i < tx.vout.size(); i++) - { - const CTxOut& txout = tx.vout[i]; - // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx - // If this matches, also add the specific output that was matched. - // This means clients don't have to update the filter themselves when a new relevant tx - // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. - CScript::const_iterator pc = txout.scriptPubKey.begin(); - vector data; - while (pc < txout.scriptPubKey.end()) - { - opcodetype opcode; - if (!txout.scriptPubKey.GetOp(pc, opcode, data)) - break; - if (data.size() != 0 && contains(data)) - { - fFound = true; - if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) - insert(COutPoint(hash, i)); - else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) - { - txnouttype type; - vector > vSolutions; - if (Solver(txout.scriptPubKey, type, vSolutions) && - (type == TX_PUBKEY || type == TX_MULTISIG)) - insert(COutPoint(hash, i)); - } - break; - } - } - } - - if (fFound) - return true; - - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - // Match if the filter contains an outpoint tx spends - if (contains(txin.prevout)) - return true; - - // Match if the filter contains any arbitrary script data element in any scriptSig in tx - CScript::const_iterator pc = txin.scriptSig.begin(); - vector data; - while (pc < txin.scriptSig.end()) - { - opcodetype opcode; - if (!txin.scriptSig.GetOp(pc, opcode, data)) - break; - if (data.size() != 0 && contains(data)) - return true; - } - } - - return false; -} diff --git a/src/bloom.h b/src/bloom.h deleted file mode 100644 index 389ae748..00000000 --- a/src/bloom.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_BLOOM_H -#define BITCOIN_BLOOM_H - -#include - -#include "uint256.h" -#include "serialize.h" - -class COutPoint; -class CTransaction; - -// 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% -static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes -static const unsigned int MAX_HASH_FUNCS = 50; - -// First two bits of nFlags control how much IsRelevantAndUpdate actually updates -// The remaining bits are reserved -enum bloomflags -{ - BLOOM_UPDATE_NONE = 0, - BLOOM_UPDATE_ALL = 1, - // Only adds outpoints to the filter if the output is a pay-to-pubkey/pay-to-multisig script - BLOOM_UPDATE_P2PUBKEY_ONLY = 2, - BLOOM_UPDATE_MASK = 3, -}; - -/** - * BloomFilter is a probabilistic filter which SPV clients provide - * so that we can filter the transactions we sends them. - * - * This allows for significantly more efficient transaction and block downloads. - * - * Because bloom filters are probabilistic, an SPV node can increase the false- - * positive rate, making us send them transactions which aren't actually theirs, - * allowing clients to trade more bandwidth for more privacy by obfuscating which - * keys are owned by them. - */ -class CBloomFilter -{ -private: - std::vector vData; - unsigned int nHashFuncs; - unsigned int nTweak; - unsigned char nFlags; - - unsigned int Hash(unsigned int nHashNum, const std::vector& vDataToHash) const; - -public: - // Creates a new bloom filter which will provide the given fp rate when filled with the given number of elements - // Note that if the given parameters will result in a filter outside the bounds of the protocol limits, - // the filter created will be as close to the given parameters as possible within the protocol limits. - // This will apply if nFPRate is very low or nElements is unreasonably high. - // nTweak is a constant which is added to the seed value passed to the hash function - // It should generally always be a random value (and is largely only exposed for unit testing) - // nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) - CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); - // Using a filter initialized with this results in undefined behavior - // Should only be used for deserialization - CBloomFilter() {} - - IMPLEMENT_SERIALIZE - ( - READWRITE(vData); - READWRITE(nHashFuncs); - READWRITE(nTweak); - READWRITE(nFlags); - ) - - void insert(const std::vector& vKey); - void insert(const COutPoint& outpoint); - void insert(const uint256& hash); - - bool contains(const std::vector& vKey) const; - bool contains(const COutPoint& outpoint) const; - bool contains(const uint256& hash) const; - - // True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS - // (catch a filter which was just deserialized which was too big) - bool IsWithinSizeConstraints() const; - - // Also adds any outputs which match the filter to the filter (to match their spending txes) - bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); -}; - -#endif /* BITCOIN_BLOOM_H */ diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp deleted file mode 100644 index 398851a1..00000000 --- a/src/checkpoints.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#include // for 'map_list_of()' -#include - -#include "checkpoints.h" - -#include "main.h" -#include "uint256.h" - -namespace Checkpoints -{ - typedef std::map MapCheckpoints; - - // How many times we expect transactions after the last checkpoint to - // be slower. This number is a compromise, as it can't be accurate for - // every system. When reindexing from a fast disk with a slow CPU, it - // can be up to 20, while when downloading from a slow network with a - // fast multicore CPU, it won't be much higher than 1. - static const double fSigcheckVerificationFactor = 5.0; - - struct CCheckpointData { - const MapCheckpoints *mapCheckpoints; - int64 nTimeLastCheckpoint; - int64 nTransactionsLastCheckpoint; - double fTransactionsPerDay; - }; - - // What makes a good checkpoint block? - // + Is surrounded by blocks with reasonable timestamps - // (no blocks before with a timestamp after, none after with - // timestamp before) - // + Contains no strange transactions - static MapCheckpoints mapCheckpoints = - boost::assign::map_list_of - ( 0, hashGenesisBlockOfficial ) - ( 74722, uint256("0x74778cd8a5f2fbacca95dc4de02771e24e365c3f28412189f349355096826afe")) - ( 84106, uint256("0xbb081d595e948203a67a7501c30d6a8c413db97deb1234185c4929f66be945ef")) - ( 85429, uint256("0xa2bd26fbebcfa02f4e291806f0214e17c2dfb250c3443bbb1778c0cd532776f4")) - ; - static const CCheckpointData data = { - &mapCheckpoints, - 1374921070, // * UNIX timestamp of last checkpoint block - 14264869, // * total number of transactions between genesis and last checkpoint - // (the tx=... number in the SetBestChain debug.log lines) - 60000.0 // * estimated number of transactions per day after checkpoint - }; - - static MapCheckpoints mapCheckpointsTestnet = - boost::assign::map_list_of - ( 0, hashGenesisBlockTestNet ) - ; - static const CCheckpointData dataTestnet = { - &mapCheckpointsTestnet, - 1338180505, - 16341, - 300 - }; - - const CCheckpointData &Checkpoints() { - if (fTestNet) - return dataTestnet; - else - return data; - } - - bool CheckBlock(int nHeight, const uint256& hash) - { - if (!GetBoolArg("-checkpoints", true)) - return true; - - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; - - MapCheckpoints::const_iterator i = checkpoints.find(nHeight); - if (i == checkpoints.end()) return true; - return hash == i->second; - } - - // Guess how far we are in the verification process at the given block index - double GuessVerificationProgress(CBlockIndex *pindex) { - if (pindex==NULL) - return 0.0; - - int64 nNow = time(NULL); - - double fWorkBefore = 0.0; // Amount of work done before pindex - double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) - // Work is defined as: 1.0 per transaction before the last checkoint, and - // fSigcheckVerificationFactor per transaction after. - - const CCheckpointData &data = Checkpoints(); - - if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { - double nCheapBefore = pindex->nChainTx; - double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; - double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; - fWorkBefore = nCheapBefore; - fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; - } else { - double nCheapBefore = data.nTransactionsLastCheckpoint; - double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; - double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay; - fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; - fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; - } - - return fWorkBefore / (fWorkBefore + fWorkAfter); - } - - int GetTotalBlocksEstimate() - { - if (!GetBoolArg("-checkpoints", true)) - return 0; - - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; - - return checkpoints.rbegin()->first; - } - - CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex) - { - if (!GetBoolArg("-checkpoints", true)) - return NULL; - - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; - - BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) - { - const uint256& hash = i.second; - std::map::const_iterator t = mapBlockIndex.find(hash); - if (t != mapBlockIndex.end()) - return t->second; - } - return NULL; - } - - uint256 GetLatestHardenedCheckpoint() - { - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; - return (checkpoints.rbegin()->second); - } -} diff --git a/src/checkpoints.h b/src/checkpoints.h deleted file mode 100644 index c1bf79e4..00000000 --- a/src/checkpoints.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_CHECKPOINT_H -#define BITCOIN_CHECKPOINT_H - -#include - -class uint256; -class CBlockIndex; - -/** Block-chain checkpoints are compiled-in sanity checks. - * They are updated every release or three. - */ -namespace Checkpoints -{ - // Returns true if block passes checkpoint checks - bool CheckBlock(int nHeight, const uint256& hash); - - // Return conservative estimate of total number of blocks, 0 if unknown - int GetTotalBlocksEstimate(); - - // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint - CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex); - - // Returns the block hash of latest hardened checkpoint - uint256 GetLatestHardenedCheckpoint(); - - double GuessVerificationProgress(CBlockIndex *pindex); -} - -#endif diff --git a/src/checkpointsync.cpp b/src/checkpointsync.cpp deleted file mode 100644 index b649a4ce..00000000 --- a/src/checkpointsync.cpp +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright (c) 2012-2013 PPCoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING -// -// The synchronized checkpoint system is first developed by Sunny King for -// ppcoin network in 2012, giving cryptocurrency developers a tool to gain -// additional network protection against 51% attack. -// -// Primecoin also adopts this security mechanism, and the enforcement of -// checkpoints is explicitly granted by user, thus granting only temporary -// consensual central control to developer at the threats of 51% attack. -// -// Concepts -// -// In the network there can be a privileged node known as 'checkpoint master'. -// This node can send out checkpoint messages signed by the checkpoint master -// key. Each checkpoint is a block hash, representing a block on the blockchain -// that the network should reach consensus on. -// -// Besides verifying signatures of checkpoint messages, each node also verifies -// the consistency of the checkpoints. If a conflicting checkpoint is received, -// it means either the checkpoint master key is compromised, or there is an -// operator mistake. In this situation the node would discard the conflicting -// checkpoint message and display a warning message. This precaution controls -// the damage to network caused by operator mistake or compromised key. -// -// Operations -// -// Checkpoint master key can be established by using the 'makekeypair' command -// The public key in source code should then be updated and private key kept -// in a safe place. -// -// Any node can be turned into checkpoint master by setting the 'checkpointkey' -// configuration parameter with the private key of the checkpoint master key. -// Operator should exercise caution such that at any moment there is at most -// one node operating as checkpoint master. When switching master node, the -// recommended procedure is to shutdown the master node and restart as -// regular node, note down the current checkpoint by 'getcheckpoint', then -// compare to the checkpoint at the new node to be upgraded to master node. -// When the checkpoint on both nodes match then it is safe to switch the new -// node to checkpoint master. -// -// The configuration parameter 'checkpointdepth' specifies how many blocks -// should the checkpoints lag behind the latest block in auto checkpoint mode. -// A depth of 0 is the strongest auto checkpoint policy and offers the greatest -// protection against 51% attack. A negative depth means that the checkpoints -// should not be automatically generated by the checkpoint master, but instead -// be manually entered by operator via the 'sendcheckpoint' command. The manual -// mode is also the default mode (default value -1 for checkpointdepth). -// -// Command 'enforcecheckpoint' and configuration parameter 'checkpointenforce' -// are for the users to explicitly consent to enforce the checkpoints issued -// from checkpoint master. To enforce checkpoint, user needs to either issue -// command 'enforcecheckpoint true', or set configuration parameter -// checkpointenforce=1. The current enforcement setting can be queried via -// command 'getcheckpoint', where 'subscribemode' displays either 'enforce' -// or 'advisory'. The 'enforce' mode of subscribemode means checkpoints are -// enforced. The 'advisory' mode of subscribemode means checkpoints are not -// enforced but a warning message would be displayed if the node is on a -// different blockchain fork from the checkpoint, and this is the default mode. -// - -#include - -#include "checkpoints.h" -#include "checkpointsync.h" - -#include "base58.h" -#include "bitcoinrpc.h" -#include "main.h" -#include "txdb.h" -#include "uint256.h" - -using namespace json_spirit; -using namespace std; - - -// ppcoin: sync-checkpoint master key -const std::string CSyncCheckpoint::strMainPubKey = "04c0c44f7e3bc58c734e65bb0e97860b2f5e53d12a5e1ea30ec6d73df821349f83ff0a061221dfcab3f1235d5b2fff85587a72f8f74f4c56d9e17087a1e8c28b04"; -const std::string CSyncCheckpoint::strTestPubKey = "0400c9611d333d6ddfc5dbfcb6ab3a7c00d56959cfda1ff2d94a8dc4c88050be18d0aa4b6c547ff4db33d708866c85616926278b05bcb80b66974280774720303b"; -std::string CSyncCheckpoint::strMasterPrivKey = ""; - - -// ppcoin: synchronized checkpoint (centrally broadcasted) -uint256 hashSyncCheckpoint = 0; -uint256 hashPendingCheckpoint = 0; -CSyncCheckpoint checkpointMessage; -CSyncCheckpoint checkpointMessagePending; -uint256 hashInvalidCheckpoint = 0; -CCriticalSection cs_hashSyncCheckpoint; -std::string strCheckpointWarning; - -// ppcoin: get last synchronized checkpoint -CBlockIndex* GetLastSyncCheckpoint() -{ - LOCK(cs_hashSyncCheckpoint); - if (!mapBlockIndex.count(hashSyncCheckpoint)) - error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); - else - return mapBlockIndex[hashSyncCheckpoint]; - return NULL; -} - -// ppcoin: only descendant of current sync-checkpoint is allowed -bool ValidateSyncCheckpoint(uint256 hashCheckpoint) -{ - if (!mapBlockIndex.count(hashSyncCheckpoint)) - return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); - if (!mapBlockIndex.count(hashCheckpoint)) - return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str()); - - CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint]; - CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint]; - - if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight) - { - // Received an older checkpoint, trace back from current checkpoint - // to the same height of the received checkpoint to verify - // that current checkpoint should be a descendant block - CBlockIndex* pindex = pindexSyncCheckpoint; - while (pindex->nHeight > pindexCheckpointRecv->nHeight) - if (!(pindex = pindex->pprev)) - return error("ValidateSyncCheckpoint: pprev1 null - block index structure failure"); - if (pindex->GetBlockHash() != hashCheckpoint) - { - hashInvalidCheckpoint = hashCheckpoint; - return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); - } - return false; // ignore older checkpoint - } - - // Received checkpoint should be a descendant block of the current - // checkpoint. Trace back to the same height of current checkpoint - // to verify. - CBlockIndex* pindex = pindexCheckpointRecv; - while (pindex->nHeight > pindexSyncCheckpoint->nHeight) - if (!(pindex = pindex->pprev)) - return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure"); - if (pindex->GetBlockHash() != hashSyncCheckpoint) - { - hashInvalidCheckpoint = hashCheckpoint; - return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); - } - return true; -} - -bool WriteSyncCheckpoint(const uint256& hashCheckpoint) -{ - if (!pblocktree->WriteSyncCheckpoint(hashCheckpoint)) - { - return error("WriteSyncCheckpoint(): failed to write to txdb sync checkpoint %s", hashCheckpoint.ToString().c_str()); - } - if (!pblocktree->Sync()) - return error("WriteSyncCheckpoint(): failed to commit to txdb sync checkpoint %s", hashCheckpoint.ToString().c_str()); - - hashSyncCheckpoint = hashCheckpoint; - return true; -} - -bool IsSyncCheckpointEnforced() -{ - return (GetBoolArg("-checkpointenforce", false) || mapArgs.count("-checkpointkey")); // checkpoint master node is always enforced -} - -bool AcceptPendingSyncCheckpoint() -{ - LOCK(cs_hashSyncCheckpoint); - if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint)) - { - if (!ValidateSyncCheckpoint(hashPendingCheckpoint)) - { - hashPendingCheckpoint = 0; - checkpointMessagePending.SetNull(); - return false; - } - - CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint]; - if (IsSyncCheckpointEnforced() && !pindexCheckpoint->IsInMainChain()) - { - CValidationState state; - if (!SetBestChain(state, pindexCheckpoint)) - { - hashInvalidCheckpoint = hashPendingCheckpoint; - return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); - } - } - - if (!WriteSyncCheckpoint(hashPendingCheckpoint)) - return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); - hashPendingCheckpoint = 0; - checkpointMessage = checkpointMessagePending; - checkpointMessagePending.SetNull(); - printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str()); - // relay the checkpoint - if (!checkpointMessage.IsNull()) - { - BOOST_FOREACH(CNode* pnode, vNodes) - checkpointMessage.RelayTo(pnode); - } - return true; - } - return false; -} - -// Automatically select a suitable sync-checkpoint -uint256 AutoSelectSyncCheckpoint() -{ - // Search backward for a block with specified depth policy - const CBlockIndex *pindex = pindexBest; - while (pindex->pprev && pindex->nHeight + (int)GetArg("-checkpointdepth", -1) > pindexBest->nHeight) - pindex = pindex->pprev; - return pindex->GetBlockHash(); -} - -// Check against synchronized checkpoint -bool CheckSyncCheckpoint(const uint256& hashBlock, const CBlockIndex* pindexPrev) -{ - int nHeight = pindexPrev->nHeight + 1; - - LOCK(cs_hashSyncCheckpoint); - // sync-checkpoint should always be accepted block - assert(mapBlockIndex.count(hashSyncCheckpoint)); - const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; - - if (nHeight > pindexSync->nHeight) - { - // trace back to same height as sync-checkpoint - const CBlockIndex* pindex = pindexPrev; - while (pindex->nHeight > pindexSync->nHeight) - if (!(pindex = pindex->pprev)) - return error("CheckSyncCheckpoint: pprev null - block index structure failure"); - if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint) - return false; // only descendant of sync-checkpoint can pass check - } - if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint) - return false; // same height with sync-checkpoint - if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock)) - return false; // lower height than sync-checkpoint - return true; -} - -bool WantedByPendingSyncCheckpoint(uint256 hashBlock) -{ - LOCK(cs_hashSyncCheckpoint); - if (hashPendingCheckpoint == 0) - return false; - if (hashBlock == hashPendingCheckpoint) - return true; - if (mapOrphanBlocks.count(hashPendingCheckpoint) - && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint])) - return true; - return false; -} - -// ppcoin: reset synchronized checkpoint to last hardened checkpoint -bool ResetSyncCheckpoint() -{ - LOCK(cs_hashSyncCheckpoint); - const uint256& hash = Checkpoints::GetLatestHardenedCheckpoint(); - if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain()) - { - // checkpoint block accepted but not yet in main chain - printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str()); - CValidationState state; - if (!SetBestChain(state, mapBlockIndex[hash])) - { - return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str()); - } - } - else if(!mapBlockIndex.count(hash)) - { - // checkpoint block not yet accepted - hashPendingCheckpoint = hash; - checkpointMessagePending.SetNull(); - printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str()); - } - - if (!WriteSyncCheckpoint((mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())? hash : hashGenesisBlock)) - return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str()); - printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str()); - return true; -} - -void AskForPendingSyncCheckpoint(CNode* pfrom) -{ - LOCK(cs_hashSyncCheckpoint); - if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint))) - pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint)); -} - -// Verify sync checkpoint master pubkey and reset sync checkpoint if changed -bool CheckCheckpointPubKey() -{ - std::string strPubKey = ""; - std::string strMasterPubKey = fTestNet? CSyncCheckpoint::strTestPubKey : CSyncCheckpoint::strMainPubKey; - if (!pblocktree->ReadCheckpointPubKey(strPubKey) || strPubKey != strMasterPubKey) - { - // write checkpoint master key to db - if (!pblocktree->WriteCheckpointPubKey(strMasterPubKey)) - return error("CheckCheckpointPubKey() : failed to write new checkpoint master key to db"); - if (!pblocktree->Sync()) - return error("CheckCheckpointPubKey() : failed to commit new checkpoint master key to db"); - if (!ResetSyncCheckpoint()) - return error("CheckCheckpointPubKey() : failed to reset sync-checkpoint"); - } - return true; -} - -bool SetCheckpointPrivKey(std::string strPrivKey) -{ - // Test signing a sync-checkpoint with genesis block - CSyncCheckpoint checkpoint; - checkpoint.hashCheckpoint = hashGenesisBlock; - CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); - sMsg << (CUnsignedSyncCheckpoint)checkpoint; - checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); - - CBitcoinSecret vchSecret; - if (!vchSecret.SetString(strPrivKey)) - return error("SendSyncCheckpoint: Checkpoint master key invalid"); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); // if key is not correct openssl may crash - if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) - return false; - - // Test signing successful, proceed - CSyncCheckpoint::strMasterPrivKey = strPrivKey; - return true; -} - -bool SendSyncCheckpoint(uint256 hashCheckpoint) -{ - CSyncCheckpoint checkpoint; - checkpoint.hashCheckpoint = hashCheckpoint; - CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); - sMsg << (CUnsignedSyncCheckpoint)checkpoint; - checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); - - if (CSyncCheckpoint::strMasterPrivKey.empty()) - return error("SendSyncCheckpoint: Checkpoint master key unavailable."); - CBitcoinSecret vchSecret; - if (!vchSecret.SetString(CSyncCheckpoint::strMasterPrivKey)) - return error("SendSyncCheckpoint: Checkpoint master key invalid"); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); // if key is not correct openssl may crash - if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) - return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?"); - - if(!checkpoint.ProcessSyncCheckpoint(NULL)) - { - printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n"); - return false; - } - - // Relay checkpoint - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - checkpoint.RelayTo(pnode); - } - return true; -} - -// Is the sync-checkpoint outside maturity window? -bool IsMatureSyncCheckpoint() -{ - LOCK(cs_hashSyncCheckpoint); - // sync-checkpoint should always be accepted block - assert(mapBlockIndex.count(hashSyncCheckpoint)); - const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; - return (nBestHeight >= pindexSync->nHeight + COINBASE_MATURITY); -} - -// Is the sync-checkpoint too old? -bool IsSyncCheckpointTooOld(unsigned int nSeconds) -{ - LOCK(cs_hashSyncCheckpoint); - // sync-checkpoint should always be accepted block - assert(mapBlockIndex.count(hashSyncCheckpoint)); - const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; - return (pindexSync->GetBlockTime() + nSeconds < GetAdjustedTime()); -} - -// ppcoin: find block wanted by given orphan block -uint256 WantedByOrphan(const CBlock* pblockOrphan) -{ - // Work back to the first block in the orphan chain - while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock)) - pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock]; - return pblockOrphan->hashPrevBlock; -} - -// ppcoin: verify signature of sync-checkpoint message -bool CSyncCheckpoint::CheckSignature() -{ - CKey key; - std::string strMasterPubKey = fTestNet? CSyncCheckpoint::strTestPubKey : CSyncCheckpoint::strMainPubKey; - if (!key.SetPubKey(ParseHex(strMasterPubKey))) - return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed"); - if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) - return error("CSyncCheckpoint::CheckSignature() : verify signature failed"); - - // Now unserialize the data - CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); - sMsg >> *(CUnsignedSyncCheckpoint*)this; - return true; -} - -// ppcoin: process synchronized checkpoint -bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom) -{ - if (!CheckSignature()) - return false; - - LOCK(cs_hashSyncCheckpoint); - if (!mapBlockIndex.count(hashCheckpoint)) - { - // We haven't received the checkpoint chain, keep the checkpoint as pending - hashPendingCheckpoint = hashCheckpoint; - checkpointMessagePending = *this; - printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str()); - // Ask this guy to fill in what we're missing - if (pfrom) - { - pfrom->PushGetBlocks(pindexBest, hashCheckpoint); - // ask directly as well in case rejected earlier by duplicate - // proof-of-stake because getblocks may not get it this time - pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint)); - } - return false; - } - - if (!ValidateSyncCheckpoint(hashCheckpoint)) - return false; - - CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint]; - if (IsSyncCheckpointEnforced() && !pindexCheckpoint->IsInMainChain()) - { - // checkpoint chain received but not yet main chain - CValidationState state; - if (!SetBestChain(state, pindexCheckpoint)) - { - hashInvalidCheckpoint = hashCheckpoint; - return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); - } - } - - if (!WriteSyncCheckpoint(hashCheckpoint)) - return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str()); - checkpointMessage = *this; - hashPendingCheckpoint = 0; - checkpointMessagePending.SetNull(); - printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str()); - return true; -} - - -// RPC commands related to sync checkpoints -// get information of sync-checkpoint (first introduced in ppcoin) -Value getcheckpoint(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getcheckpoint\n" - "Show info of synchronized checkpoint.\n"); - - Object result; - CBlockIndex* pindexCheckpoint; - - result.push_back(Pair("synccheckpoint", hashSyncCheckpoint.ToString().c_str())); - if (mapBlockIndex.count(hashSyncCheckpoint)) - { - pindexCheckpoint = mapBlockIndex[hashSyncCheckpoint]; - result.push_back(Pair("height", pindexCheckpoint->nHeight)); - result.push_back(Pair("timestamp", (boost::int64_t) pindexCheckpoint->GetBlockTime())); - } - result.push_back(Pair("subscribemode", IsSyncCheckpointEnforced()? "enforce" : "advisory")); - if (mapArgs.count("-checkpointkey")) - result.push_back(Pair("checkpointmaster", true)); - - return result; -} - -Value sendcheckpoint(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "sendcheckpoint \n" - "Send a synchronized checkpoint.\n"); - - if (!mapArgs.count("-checkpointkey") || CSyncCheckpoint::strMasterPrivKey.empty()) - throw runtime_error("Not a checkpointmaster node, first set checkpointkey in configuration and restart client. "); - - std::string strHash = params[0].get_str(); - uint256 hash(strHash); - - if (!SendSyncCheckpoint(hash)) - throw runtime_error("Failed to send checkpoint, check log. "); - - Object result; - CBlockIndex* pindexCheckpoint; - - result.push_back(Pair("synccheckpoint", hashSyncCheckpoint.ToString().c_str())); - if (mapBlockIndex.count(hashSyncCheckpoint)) - { - pindexCheckpoint = mapBlockIndex[hashSyncCheckpoint]; - result.push_back(Pair("height", pindexCheckpoint->nHeight)); - result.push_back(Pair("timestamp", (boost::int64_t) pindexCheckpoint->GetBlockTime())); - } - result.push_back(Pair("subscribemode", IsSyncCheckpointEnforced()? "enforce" : "advisory")); - if (mapArgs.count("-checkpointkey")) - result.push_back(Pair("checkpointmaster", true)); - - return result; -} - -Value enforcecheckpoint(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "enforcecheckpoint \n" - " is true or false to enable or disable enforcement of broadcasted checkpoints by developer."); - - bool fEnforceCheckpoint = params[0].get_bool(); - if (mapArgs.count("-checkpointkey") && !fEnforceCheckpoint) - throw runtime_error( - "checkpoint master node must enforce synchronized checkpoints."); - if (fEnforceCheckpoint) - strCheckpointWarning = ""; - mapArgs["-checkpointenforce"] = (fEnforceCheckpoint ? "1" : "0"); - return Value::null; -} - diff --git a/src/checkpointsync.h b/src/checkpointsync.h deleted file mode 100644 index 82d51a2b..00000000 --- a/src/checkpointsync.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2011-2013 PPCoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 open source software license -// see the accompanying file COPYING -#ifndef PRIMECOIN_CHECKPOINTSYNC_H -#define PRIMECOIN_CHECKPOINTSYNC_H - -#include "net.h" -#include "util.h" - -#define CHECKPOINT_MAX_SPAN (60 * 60 * 4) // max 4 hours before latest block - -class uint256; -class CBlock; -class CBlockIndex; -class CSyncCheckpoint; - -extern uint256 hashSyncCheckpoint; -extern CSyncCheckpoint checkpointMessage; -extern uint256 hashInvalidCheckpoint; -extern CCriticalSection cs_hashSyncCheckpoint; -extern std::string strCheckpointWarning; - -CBlockIndex* GetLastSyncCheckpoint(); -bool WriteSyncCheckpoint(const uint256& hashCheckpoint); -bool IsSyncCheckpointEnforced(); -bool AcceptPendingSyncCheckpoint(); -uint256 AutoSelectSyncCheckpoint(); -bool CheckSyncCheckpoint(const uint256& hashBlock, const CBlockIndex* pindexPrev); -bool WantedByPendingSyncCheckpoint(uint256 hashBlock); -bool ResetSyncCheckpoint(); -void AskForPendingSyncCheckpoint(CNode* pfrom); -bool CheckCheckpointPubKey(); -bool SetCheckpointPrivKey(std::string strPrivKey); -bool SendSyncCheckpoint(uint256 hashCheckpoint); -bool IsMatureSyncCheckpoint(); -bool IsSyncCheckpointTooOld(unsigned int nSeconds); -uint256 WantedByOrphan(const CBlock* pblockOrphan); - -// Synchronized checkpoint (introduced first in ppcoin) -class CUnsignedSyncCheckpoint -{ -public: - int nVersion; - uint256 hashCheckpoint; // checkpoint block - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(hashCheckpoint); - ) - - void SetNull() - { - nVersion = 1; - hashCheckpoint = 0; - } - - std::string ToString() const - { - return strprintf( - "CSyncCheckpoint(\n" - " nVersion = %d\n" - " hashCheckpoint = %s\n" - ")\n", - nVersion, - hashCheckpoint.ToString().c_str()); - } - - void print() const - { - printf("%s", ToString().c_str()); - } -}; - -class CSyncCheckpoint : public CUnsignedSyncCheckpoint -{ -public: - static const std::string strMainPubKey; - static const std::string strTestPubKey; - static std::string strMasterPrivKey; - - std::vector vchMsg; - std::vector vchSig; - - CSyncCheckpoint() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(vchMsg); - READWRITE(vchSig); - ) - - void SetNull() - { - CUnsignedSyncCheckpoint::SetNull(); - vchMsg.clear(); - vchSig.clear(); - } - - bool IsNull() const - { - return (hashCheckpoint == 0); - } - - uint256 GetHash() const - { - return Hash(this->vchMsg.begin(), this->vchMsg.end()); - } - - bool RelayTo(CNode* pnode) const - { - // returns true if wasn't already sent - if (pnode->hashCheckpointKnown != hashCheckpoint) - { - pnode->hashCheckpointKnown = hashCheckpoint; - pnode->PushMessage("checkpoint", *this); - return true; - } - return false; - } - - bool CheckSignature(); - bool ProcessSyncCheckpoint(CNode* pfrom); -}; - -#endif diff --git a/src/checkqueue.h b/src/checkqueue.h deleted file mode 100644 index 4be68d16..00000000 --- a/src/checkqueue.h +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef CHECKQUEUE_H -#define CHECKQUEUE_H - -#include -#include -#include - -#include -#include - -template class CCheckQueueControl; - -/** Queue for verifications that have to be performed. - * The verifications are represented by a type T, which must provide an - * operator(), returning a bool. - * - * One thread (the master) is assumed to push batches of verifications - * onto the queue, where they are processed by N-1 worker threads. When - * the master is done adding work, it temporarily joins the worker pool - * as an N'th worker, until all jobs are done. - */ -template class CCheckQueue { -private: - // Mutex to allow only one control class access at a time - boost::mutex controlMutex; - - // Mutex to protect the inner state - boost::mutex mutex; - - // Worker threads block on this when out of work - boost::condition_variable condWorker; - - // Master thread blocks on this when out of work - boost::condition_variable condMaster; - - // The queue of elements to be processed. - // As the order of booleans doesn't matter, it is used as a LIFO (stack) - std::vector queue; - - // The number of workers (including the master) that are idle. - int nIdle; - - // The total number of workers (including the master). - int nTotal; - - // The temporary evaluation result. - bool fAllOk; - - // Number of verifications that haven't completed yet. - // This includes elements that are not anymore in queue, but still in - // worker's own batches. - unsigned int nTodo; - - // Whether we're shutting down. - bool fQuit; - - // The maximum number of elements to be processed in one batch - unsigned int nBatchSize; - - // Internal function that does bulk of the verification work. - bool Loop(bool fMaster = false) { - boost::condition_variable &cond = fMaster ? condMaster : condWorker; - std::vector vChecks; - vChecks.reserve(nBatchSize); - unsigned int nNow = 0; - bool fOk = true; - do { - { - boost::unique_lock lock(mutex); - // first do the clean-up of the previous loop run (allowing us to do it in the same critsect) - if (nNow) { - fAllOk &= fOk; - nTodo -= nNow; - if (nTodo == 0 && !fMaster) - // We processed the last element; inform the master he can exit and return the result - condMaster.notify_one(); - } else { - // first iteration - nTotal++; - } - // logically, the do loop starts here - while (queue.empty()) { - if ((fMaster || fQuit) && nTodo == 0) { - nTotal--; - bool fRet = fAllOk; - // reset the status for new work later - if (fMaster) - fAllOk = true; - // return the current status - return fRet; - } - nIdle++; - cond.wait(lock); // wait - nIdle--; - } - // Decide how many work units to process now. - // * Do not try to do everything at once, but aim for increasingly smaller batches so - // all workers finish approximately simultaneously. - // * Try to account for idle jobs which will instantly start helping. - // * Don't do batches smaller than 1 (duh), or larger than nBatchSize. - nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1))); - vChecks.resize(nNow); - for (unsigned int i = 0; i < nNow; i++) { - // We want the lock on the mutex to be as short as possible, so swap jobs from the global - // queue to the local batch vector instead of copying. - vChecks[i].swap(queue.back()); - queue.pop_back(); - } - // Check whether we need to do work at all - fOk = fAllOk; - } - // execute work - BOOST_FOREACH(T &check, vChecks) - if (fOk) - fOk = check(); - vChecks.clear(); - } while(true); - } - -public: - // Create a new check queue - CCheckQueue(unsigned int nBatchSizeIn) : - nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} - - // Worker thread - void Thread() { - Loop(); - } - - // Wait until execution finishes, and return whether all evaluations where succesful. - bool Wait() { - return Loop(true); - } - - // Add a batch of checks to the queue - void Add(std::vector &vChecks) { - boost::unique_lock lock(mutex); - BOOST_FOREACH(T &check, vChecks) { - queue.push_back(T()); - check.swap(queue.back()); - } - nTodo += vChecks.size(); - if (vChecks.size() == 1) - condWorker.notify_one(); - else if (vChecks.size() > 1) - condWorker.notify_all(); - } - - ~CCheckQueue() { - } - - friend class CCheckQueueControl; -}; - -/** RAII-style controller object for a CCheckQueue that guarantees the passed - * queue is finished before continuing. - */ -template class CCheckQueueControl { -private: - CCheckQueue *pqueue; - bool fDone; - -public: - CCheckQueueControl(CCheckQueue *pqueueIn) : pqueue(pqueueIn), fDone(false) { - // passed queue is supposed to be unused, or NULL - if (pqueue != NULL) { - pqueue->controlMutex.lock(); - assert(pqueue->nTotal == pqueue->nIdle); - assert(pqueue->nTodo == 0); - assert(pqueue->fAllOk == true); - } - } - - bool Wait() { - if (pqueue == NULL) - return true; - bool fRet = pqueue->Wait(); - pqueue->controlMutex.unlock(); - fDone = true; - return fRet; - } - - void Add(std::vector &vChecks) { - if (pqueue != NULL) - pqueue->Add(vChecks); - } - - ~CCheckQueueControl() { - if (!fDone) - Wait(); - } -}; - -#endif diff --git a/src/compat.h b/src/compat.h index 0b934204..d4e7c749 100644 --- a/src/compat.h +++ b/src/compat.h @@ -28,7 +28,10 @@ #include #include #include +#include +#include #endif +typedef unsigned long long uint64; #ifdef __sun__ using std::map; diff --git a/src/crypter.cpp b/src/crypter.cpp deleted file mode 100644 index a2b62a87..00000000 --- a/src/crypter.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2009-2012 The Bitcoin Developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#ifdef WIN32 -#include -#endif - -#include "crypter.h" - -bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) -{ - if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) - return false; - - int i = 0; - if (nDerivationMethod == 0) - i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], - (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV); - - if (i != (int)WALLET_CRYPTO_KEY_SIZE) - { - OPENSSL_cleanse(chKey, sizeof(chKey)); - OPENSSL_cleanse(chIV, sizeof(chIV)); - return false; - } - - fKeySet = true; - return true; -} - -bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV) -{ - if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE) - return false; - - memcpy(&chKey[0], &chNewKey[0], sizeof chKey); - memcpy(&chIV[0], &chNewIV[0], sizeof chIV); - - fKeySet = true; - return true; -} - -bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext) -{ - if (!fKeySet) - return false; - - // max ciphertext len for a n bytes of plaintext is - // n + AES_BLOCK_SIZE - 1 bytes - int nLen = vchPlaintext.size(); - int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0; - vchCiphertext = std::vector (nCLen); - - EVP_CIPHER_CTX ctx; - - bool fOk = true; - - EVP_CIPHER_CTX_init(&ctx); - if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV); - if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen); - if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen); - EVP_CIPHER_CTX_cleanup(&ctx); - - if (!fOk) return false; - - vchCiphertext.resize(nCLen + nFLen); - return true; -} - -bool CCrypter::Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) -{ - if (!fKeySet) - return false; - - // plaintext will always be equal to or lesser than length of ciphertext - int nLen = vchCiphertext.size(); - int nPLen = nLen, nFLen = 0; - - vchPlaintext = CKeyingMaterial(nPLen); - - EVP_CIPHER_CTX ctx; - - bool fOk = true; - - EVP_CIPHER_CTX_init(&ctx); - if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV); - if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen); - if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen); - EVP_CIPHER_CTX_cleanup(&ctx); - - if (!fOk) return false; - - vchPlaintext.resize(nPLen + nFLen); - return true; -} - - -bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext) -{ - CCrypter cKeyCrypter; - std::vector chIV(WALLET_CRYPTO_KEY_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); - if(!cKeyCrypter.SetKey(vMasterKey, chIV)) - return false; - return cKeyCrypter.Encrypt((CKeyingMaterial)vchPlaintext, vchCiphertext); -} - -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CSecret& vchPlaintext) -{ - CCrypter cKeyCrypter; - std::vector chIV(WALLET_CRYPTO_KEY_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); - if(!cKeyCrypter.SetKey(vMasterKey, chIV)) - return false; - return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); -} diff --git a/src/crypter.h b/src/crypter.h deleted file mode 100644 index 6f75170b..00000000 --- a/src/crypter.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2009-2012 The Bitcoin Developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef __CRYPTER_H__ -#define __CRYPTER_H__ - -#include "allocators.h" /* for SecureString */ -#include "key.h" -#include "serialize.h" - -const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; -const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; - -/* -Private key encryption is done based on a CMasterKey, -which holds a salt and random encryption key. - -CMasterKeys are encrypted using AES-256-CBC using a key -derived using derivation method nDerivationMethod -(0 == EVP_sha512()) and derivation iterations nDeriveIterations. -vchOtherDerivationParameters is provided for alternative algorithms -which may require more parameters (such as scrypt). - -Wallet Private Keys are then encrypted using AES-256-CBC -with the double-sha256 of the public key as the IV, and the -master key's key as the encryption key (see keystore.[ch]). -*/ - -/** Master key for wallet encryption */ -class CMasterKey -{ -public: - std::vector vchCryptedKey; - std::vector vchSalt; - // 0 = EVP_sha512() - // 1 = scrypt() - unsigned int nDerivationMethod; - unsigned int nDeriveIterations; - // Use this for more parameters to key derivation, - // such as the various parameters to scrypt - std::vector vchOtherDerivationParameters; - - IMPLEMENT_SERIALIZE - ( - READWRITE(vchCryptedKey); - READWRITE(vchSalt); - READWRITE(nDerivationMethod); - READWRITE(nDeriveIterations); - READWRITE(vchOtherDerivationParameters); - ) - CMasterKey() - { - // 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M - // ie slightly lower than the lowest hardware we need bother supporting - nDeriveIterations = 25000; - nDerivationMethod = 0; - vchOtherDerivationParameters = std::vector(0); - } -}; - -typedef std::vector > CKeyingMaterial; - -/** Encryption/decryption context with key information */ -class CCrypter -{ -private: - unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; - unsigned char chIV[WALLET_CRYPTO_KEY_SIZE]; - bool fKeySet; - -public: - bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); - bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext); - bool Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext); - bool SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV); - - void CleanKey() - { - OPENSSL_cleanse(chKey, sizeof(chKey)); - OPENSSL_cleanse(chIV, sizeof(chIV)); - fKeySet = false; - } - - CCrypter() - { - fKeySet = false; - - // Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap) - // Note that this does nothing about suspend-to-disk (which will put all our key data on disk) - // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process. - LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey); - LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV); - } - - ~CCrypter() - { - CleanKey(); - - LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey); - LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV); - } -}; - -bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext); - -#endif diff --git a/src/db.cpp b/src/db.cpp deleted file mode 100644 index 3133d99b..00000000 --- a/src/db.cpp +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "db.h" -#include "util.h" -#include "main.h" -#include -#include -#include - -#ifndef WIN32 -#include "sys/stat.h" -#endif - -using namespace std; -using namespace boost; - - -unsigned int nWalletDBUpdated; - - - -// -// CDB -// - -CDBEnv bitdb; - -void CDBEnv::EnvShutdown() -{ - if (!fDbEnvInit) - return; - - fDbEnvInit = false; - int ret = dbenv.close(0); - if (ret != 0) - printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); - if (!fMockDb) - DbEnv(0).remove(path.string().c_str(), 0); -} - -CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) -{ - fDbEnvInit = false; - fMockDb = false; -} - -CDBEnv::~CDBEnv() -{ - EnvShutdown(); -} - -void CDBEnv::Close() -{ - EnvShutdown(); -} - -bool CDBEnv::Open(const boost::filesystem::path& pathIn) -{ - if (fDbEnvInit) - return true; - - boost::this_thread::interruption_point(); - - path = pathIn; - filesystem::path pathLogDir = path / "database"; - filesystem::create_directory(pathLogDir); - filesystem::path pathErrorFile = path / "db.log"; - printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); - - unsigned int nEnvFlags = 0; - if (GetBoolArg("-privdb", true)) - nEnvFlags |= DB_PRIVATE; - - dbenv.set_lg_dir(pathLogDir.string().c_str()); - dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet - dbenv.set_lg_bsize(0x10000); - dbenv.set_lg_max(1048576); - dbenv.set_lk_max_locks(40000); - dbenv.set_lk_max_objects(40000); - dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug - dbenv.set_flags(DB_AUTO_COMMIT, 1); - dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); - dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); - int ret = dbenv.open(path.string().c_str(), - DB_CREATE | - DB_INIT_LOCK | - DB_INIT_LOG | - DB_INIT_MPOOL | - DB_INIT_TXN | - DB_THREAD | - DB_RECOVER | - nEnvFlags, - S_IRUSR | S_IWUSR); - if (ret != 0) - return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret); - - fDbEnvInit = true; - fMockDb = false; - return true; -} - -void CDBEnv::MakeMock() -{ - if (fDbEnvInit) - throw runtime_error("CDBEnv::MakeMock(): already initialized"); - - boost::this_thread::interruption_point(); - - printf("CDBEnv::MakeMock()\n"); - - dbenv.set_cachesize(1, 0, 1); - dbenv.set_lg_bsize(10485760*4); - dbenv.set_lg_max(10485760); - dbenv.set_lk_max_locks(10000); - dbenv.set_lk_max_objects(10000); - dbenv.set_flags(DB_AUTO_COMMIT, 1); - dbenv.log_set_config(DB_LOG_IN_MEMORY, 1); - int ret = dbenv.open(NULL, - DB_CREATE | - DB_INIT_LOCK | - DB_INIT_LOG | - DB_INIT_MPOOL | - DB_INIT_TXN | - DB_THREAD | - DB_PRIVATE, - S_IRUSR | S_IWUSR); - if (ret > 0) - throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret)); - - fDbEnvInit = true; - fMockDb = true; -} - -CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)) -{ - LOCK(cs_db); - assert(mapFileUseCount.count(strFile) == 0); - - Db db(&dbenv, 0); - int result = db.verify(strFile.c_str(), NULL, NULL, 0); - if (result == 0) - return VERIFY_OK; - else if (recoverFunc == NULL) - return RECOVER_FAIL; - - // Try to recover: - bool fRecovered = (*recoverFunc)(*this, strFile); - return (fRecovered ? RECOVER_OK : RECOVER_FAIL); -} - -bool CDBEnv::Salvage(std::string strFile, bool fAggressive, - std::vector& vResult) -{ - LOCK(cs_db); - assert(mapFileUseCount.count(strFile) == 0); - - u_int32_t flags = DB_SALVAGE; - if (fAggressive) flags |= DB_AGGRESSIVE; - - stringstream strDump; - - Db db(&dbenv, 0); - int result = db.verify(strFile.c_str(), NULL, &strDump, flags); - if (result == DB_VERIFY_BAD) - { - printf("Error: Salvage found errors, all data may not be recoverable.\n"); - if (!fAggressive) - { - printf("Error: Rerun with aggressive mode to ignore errors and continue.\n"); - return false; - } - } - if (result != 0 && result != DB_VERIFY_BAD) - { - printf("ERROR: db salvage failed: %d\n",result); - return false; - } - - // Format of bdb dump is ascii lines: - // header lines... - // HEADER=END - // hexadecimal key - // hexadecimal value - // ... repeated - // DATA=END - - string strLine; - while (!strDump.eof() && strLine != "HEADER=END") - getline(strDump, strLine); // Skip past header - - std::string keyHex, valueHex; - while (!strDump.eof() && keyHex != "DATA=END") - { - getline(strDump, keyHex); - if (keyHex != "DATA_END") - { - getline(strDump, valueHex); - vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex))); - } - } - - return (result == 0); -} - - -void CDBEnv::CheckpointLSN(std::string strFile) -{ - dbenv.txn_checkpoint(0, 0, 0); - if (fMockDb) - return; - dbenv.lsn_reset(strFile.c_str(), 0); -} - - -CDB::CDB(const char *pszFile, const char* pszMode) : - pdb(NULL), activeTxn(NULL) -{ - int ret; - if (pszFile == NULL) - return; - - fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); - bool fCreate = strchr(pszMode, 'c'); - unsigned int nFlags = DB_THREAD; - if (fCreate) - nFlags |= DB_CREATE; - - { - LOCK(bitdb.cs_db); - if (!bitdb.Open(GetDataDir())) - throw runtime_error("env open failed"); - - strFile = pszFile; - ++bitdb.mapFileUseCount[strFile]; - pdb = bitdb.mapDb[strFile]; - if (pdb == NULL) - { - pdb = new Db(&bitdb.dbenv, 0); - - bool fMockDb = bitdb.IsMock(); - if (fMockDb) - { - DbMpoolFile*mpf = pdb->get_mpf(); - ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); - if (ret != 0) - throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile)); - } - - ret = pdb->open(NULL, // Txn pointer - fMockDb ? NULL : pszFile, // Filename - fMockDb ? pszFile : "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags - 0); - - if (ret != 0) - { - delete pdb; - pdb = NULL; - --bitdb.mapFileUseCount[strFile]; - strFile = ""; - throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); - } - - if (fCreate && !Exists(string("version"))) - { - bool fTmp = fReadOnly; - fReadOnly = false; - WriteVersion(CLIENT_VERSION); - fReadOnly = fTmp; - } - - bitdb.mapDb[strFile] = pdb; - } - } -} - -void CDB::Flush() -{ - if (activeTxn) - return; - - // Flush database activity from memory pool to disk log - unsigned int nMinutes = 0; - if (fReadOnly) - nMinutes = 1; - - bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); -} - -void CDB::Close() -{ - if (!pdb) - return; - if (activeTxn) - activeTxn->abort(); - activeTxn = NULL; - pdb = NULL; - - Flush(); - - { - LOCK(bitdb.cs_db); - --bitdb.mapFileUseCount[strFile]; - } -} - -void CDBEnv::CloseDb(const string& strFile) -{ - { - LOCK(cs_db); - if (mapDb[strFile] != NULL) - { - // Close the database handle - Db* pdb = mapDb[strFile]; - pdb->close(0); - delete pdb; - mapDb[strFile] = NULL; - } - } -} - -bool CDBEnv::RemoveDb(const string& strFile) -{ - this->CloseDb(strFile); - - LOCK(cs_db); - int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); - return (rc == 0); -} - -bool CDB::Rewrite(const string& strFile, const char* pszSkip) -{ - while (true) - { - { - LOCK(bitdb.cs_db); - if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) - { - // Flush log data to the dat file - bitdb.CloseDb(strFile); - bitdb.CheckpointLSN(strFile); - bitdb.mapFileUseCount.erase(strFile); - - bool fSuccess = true; - printf("Rewriting %s...\n", strFile.c_str()); - string strFileRes = strFile + ".rewrite"; - { // surround usage of db with extra {} - CDB db(strFile.c_str(), "r"); - Db* pdbCopy = new Db(&bitdb.dbenv, 0); - - int ret = pdbCopy->open(NULL, // Txn pointer - strFileRes.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - DB_CREATE, // Flags - 0); - if (ret > 0) - { - printf("Cannot create database file %s\n", strFileRes.c_str()); - fSuccess = false; - } - - Dbc* pcursor = db.GetCursor(); - if (pcursor) - while (fSuccess) - { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); - if (ret == DB_NOTFOUND) - { - pcursor->close(); - break; - } - else if (ret != 0) - { - pcursor->close(); - fSuccess = false; - break; - } - if (pszSkip && - strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) - continue; - if (strncmp(&ssKey[0], "\x07version", 8) == 0) - { - // Update version: - ssValue.clear(); - ssValue << CLIENT_VERSION; - } - Dbt datKey(&ssKey[0], ssKey.size()); - Dbt datValue(&ssValue[0], ssValue.size()); - int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); - if (ret2 > 0) - fSuccess = false; - } - if (fSuccess) - { - db.Close(); - bitdb.CloseDb(strFile); - if (pdbCopy->close(0)) - fSuccess = false; - delete pdbCopy; - } - } - if (fSuccess) - { - Db dbA(&bitdb.dbenv, 0); - if (dbA.remove(strFile.c_str(), NULL, 0)) - fSuccess = false; - Db dbB(&bitdb.dbenv, 0); - if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) - fSuccess = false; - } - if (!fSuccess) - printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); - return fSuccess; - } - } - MilliSleep(100); - } - return false; -} - - -void CDBEnv::Flush(bool fShutdown) -{ - int64 nStart = GetTimeMillis(); - // Flush log data to the actual data file - // on all files that are not in use - printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); - if (!fDbEnvInit) - return; - { - LOCK(cs_db); - map::iterator mi = mapFileUseCount.begin(); - while (mi != mapFileUseCount.end()) - { - string strFile = (*mi).first; - int nRefCount = (*mi).second; - printf("%s refcount=%d\n", strFile.c_str(), nRefCount); - if (nRefCount == 0) - { - // Move log data to the dat file - CloseDb(strFile); - printf("%s checkpoint\n", strFile.c_str()); - dbenv.txn_checkpoint(0, 0, 0); - printf("%s detach\n", strFile.c_str()); - if (!fMockDb) - dbenv.lsn_reset(strFile.c_str(), 0); - printf("%s closed\n", strFile.c_str()); - mapFileUseCount.erase(mi++); - } - else - mi++; - } - printf("DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart); - if (fShutdown) - { - char** listp; - if (mapFileUseCount.empty()) - { - dbenv.log_archive(&listp, DB_ARCH_REMOVE); - Close(); - if (!fMockDb) - boost::filesystem::remove_all(path / "database"); - } - } - } -} - - - - - - - - - - - -// -// CAddrDB -// - - -CAddrDB::CAddrDB() -{ - pathAddr = GetDataDir() / "peers.dat"; -} - -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - RAND_bytes((unsigned char *)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); - - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(pchMessageStart); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CAddrman::Write() : open failed"); - - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (std::exception &e) { - return error("CAddrman::Write() : I/O error"); - } - FileCommit(fileout); - fileout.fclose(); - - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("CAddrman::Write() : Rename-into-place failed"); - - return true; -} - -bool CAddrDB::Read(CAddrMan& addr) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathAddr.string().c_str(), "rb"); - CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CAddrman::Read() : open failed"); - - // use file size to size memory buffer - int fileSize = GetFilesize(filein); - int dataSize = fileSize - sizeof(uint256); - vector vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (std::exception &e) { - return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("CAddrman::Read() : checksum mismatch; data corrupted"); - - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (pchMessageStart magic number) and - ssPeers >> FLATDATA(pchMsgTmp); - - // verify the network matches ours - if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp))) - return error("CAddrman::Read() : invalid network magic number"); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (std::exception &e) { - return error("CAddrman::Read() : I/O error or stream data corrupted"); - } - - return true; -} - diff --git a/src/db.h b/src/db.h deleted file mode 100644 index ea440c49..00000000 --- a/src/db.h +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_DB_H -#define BITCOIN_DB_H - -#include "main.h" - -#include -#include -#include - -#include - -class CAddress; -class CAddrMan; -class CBlockLocator; -class CDiskBlockIndex; -class CMasterKey; -class COutPoint; -class CWallet; -class CWalletTx; - -extern unsigned int nWalletDBUpdated; - -void ThreadFlushWalletDB(const std::string& strWalletFile); -bool BackupWallet(const CWallet& wallet, const std::string& strDest); - - -class CDBEnv -{ -private: - bool fDbEnvInit; - bool fMockDb; - boost::filesystem::path path; - - void EnvShutdown(); - -public: - mutable CCriticalSection cs_db; - DbEnv dbenv; - std::map mapFileUseCount; - std::map mapDb; - - CDBEnv(); - ~CDBEnv(); - void MakeMock(); - bool IsMock() { return fMockDb; } - - /* - * Verify that database file strFile is OK. If it is not, - * call the callback to try to recover. - * This must be called BEFORE strFile is opened. - * Returns true if strFile is OK. - */ - enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; - VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)); - /* - * Salvage data from a file that Verify says is bad. - * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). - * Appends binary key/value pairs to vResult, returns true if successful. - * NOTE: reads the entire database into memory, so cannot be used - * for huge databases. - */ - typedef std::pair, std::vector > KeyValPair; - bool Salvage(std::string strFile, bool fAggressive, std::vector& vResult); - - bool Open(const boost::filesystem::path &path); - void Close(); - void Flush(bool fShutdown); - void CheckpointLSN(std::string strFile); - - void CloseDb(const std::string& strFile); - bool RemoveDb(const std::string& strFile); - - DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC) - { - DbTxn* ptxn = NULL; - int ret = dbenv.txn_begin(NULL, &ptxn, flags); - if (!ptxn || ret != 0) - return NULL; - return ptxn; - } -}; - -extern CDBEnv bitdb; - - -/** RAII class that provides access to a Berkeley database */ -class CDB -{ -protected: - Db* pdb; - std::string strFile; - DbTxn *activeTxn; - bool fReadOnly; - - explicit CDB(const char* pszFile, const char* pszMode="r+"); - ~CDB() { Close(); } -public: - void Flush(); - void Close(); -private: - CDB(const CDB&); - void operator=(const CDB&); - -protected: - template - bool Read(const K& key, T& value) - { - if (!pdb) - return false; - - // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Read - Dbt datValue; - datValue.set_flags(DB_DBT_MALLOC); - int ret = pdb->get(activeTxn, &datKey, &datValue, 0); - memset(datKey.get_data(), 0, datKey.get_size()); - if (datValue.get_data() == NULL) - return false; - - // Unserialize value - try { - CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); - ssValue >> value; - } - catch (std::exception &e) { - return false; - } - - // Clear and free memory - memset(datValue.get_data(), 0, datValue.get_size()); - free(datValue.get_data()); - return (ret == 0); - } - - template - bool Write(const K& key, const T& value, bool fOverwrite=true) - { - if (!pdb) - return false; - if (fReadOnly) - assert(!"Write called on database in read-only mode"); - - // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Value - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(10000); - ssValue << value; - Dbt datValue(&ssValue[0], ssValue.size()); - - // Write - int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); - - // Clear memory in case it was a private key - memset(datKey.get_data(), 0, datKey.get_size()); - memset(datValue.get_data(), 0, datValue.get_size()); - return (ret == 0); - } - - template - bool Erase(const K& key) - { - if (!pdb) - return false; - if (fReadOnly) - assert(!"Erase called on database in read-only mode"); - - // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Erase - int ret = pdb->del(activeTxn, &datKey, 0); - - // Clear memory - memset(datKey.get_data(), 0, datKey.get_size()); - return (ret == 0 || ret == DB_NOTFOUND); - } - - template - bool Exists(const K& key) - { - if (!pdb) - return false; - - // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Exists - int ret = pdb->exists(activeTxn, &datKey, 0); - - // Clear memory - memset(datKey.get_data(), 0, datKey.get_size()); - return (ret == 0); - } - - Dbc* GetCursor() - { - if (!pdb) - return NULL; - Dbc* pcursor = NULL; - int ret = pdb->cursor(NULL, &pcursor, 0); - if (ret != 0) - return NULL; - return pcursor; - } - - int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) - { - // Read at cursor - Dbt datKey; - if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) - { - datKey.set_data(&ssKey[0]); - datKey.set_size(ssKey.size()); - } - Dbt datValue; - if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) - { - datValue.set_data(&ssValue[0]); - datValue.set_size(ssValue.size()); - } - datKey.set_flags(DB_DBT_MALLOC); - datValue.set_flags(DB_DBT_MALLOC); - int ret = pcursor->get(&datKey, &datValue, fFlags); - if (ret != 0) - return ret; - else if (datKey.get_data() == NULL || datValue.get_data() == NULL) - return 99999; - - // Convert to streams - ssKey.SetType(SER_DISK); - ssKey.clear(); - ssKey.write((char*)datKey.get_data(), datKey.get_size()); - ssValue.SetType(SER_DISK); - ssValue.clear(); - ssValue.write((char*)datValue.get_data(), datValue.get_size()); - - // Clear and free memory - memset(datKey.get_data(), 0, datKey.get_size()); - memset(datValue.get_data(), 0, datValue.get_size()); - free(datKey.get_data()); - free(datValue.get_data()); - return 0; - } - -public: - bool TxnBegin() - { - if (!pdb || activeTxn) - return false; - DbTxn* ptxn = bitdb.TxnBegin(); - if (!ptxn) - return false; - activeTxn = ptxn; - return true; - } - - bool TxnCommit() - { - if (!pdb || !activeTxn) - return false; - int ret = activeTxn->commit(0); - activeTxn = NULL; - return (ret == 0); - } - - bool TxnAbort() - { - if (!pdb || !activeTxn) - return false; - int ret = activeTxn->abort(); - activeTxn = NULL; - return (ret == 0); - } - - bool ReadVersion(int& nVersion) - { - nVersion = 0; - return Read(std::string("version"), nVersion); - } - - bool WriteVersion(int nVersion) - { - return Write(std::string("version"), nVersion); - } - - bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); -}; - - - - - - - - -/** Access to the (IP) address database (peers.dat) */ -class CAddrDB -{ -private: - boost::filesystem::path pathAddr; -public: - CAddrDB(); - bool Write(const CAddrMan& addr); - bool Read(CAddrMan& addr); -}; - -#endif // BITCOIN_DB_H diff --git a/src/hash.cpp b/src/hash.cpp deleted file mode 100644 index bddd8abf..00000000 --- a/src/hash.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "hash.h" - -inline uint32_t ROTL32 ( uint32_t x, int8_t r ) -{ - return (x << r) | (x >> (32 - r)); -} - -unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash) -{ - // The following is MurmurHash3 (x86_32), see http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - uint32_t h1 = nHashSeed; - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; - - const int nblocks = vDataToHash.size() / 4; - - //---------- - // body - const uint32_t * blocks = (const uint32_t *)(&vDataToHash[0] + nblocks*4); - - for(int i = -nblocks; i; i++) - { - uint32_t k1 = blocks[i]; - - k1 *= c1; - k1 = ROTL32(k1,15); - k1 *= c2; - - h1 ^= k1; - h1 = ROTL32(h1,13); - h1 = h1*5+0xe6546b64; - } - - //---------- - // tail - const uint8_t * tail = (const uint8_t*)(&vDataToHash[0] + nblocks*4); - - uint32_t k1 = 0; - - switch(vDataToHash.size() & 3) - { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; - }; - - //---------- - // finalization - h1 ^= vDataToHash.size(); - h1 ^= h1 >> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >> 16; - - return h1; -} diff --git a/src/hash.h b/src/hash.h index eaa1780c..f0fe02f3 100644 --- a/src/hash.h +++ b/src/hash.h @@ -6,7 +6,6 @@ #define BITCOIN_HASH_H #include "uint256.h" -#include "serialize.h" #include #include @@ -53,13 +52,6 @@ public: SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); return hash2; } - - template - CHashWriter& operator<<(const T& obj) { - // Serialize to this stream - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } }; @@ -97,14 +89,6 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, return hash2; } -template -uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION) -{ - CHashWriter ss(nType, nVersion); - ss << obj; - return ss.GetHash(); -} - inline uint160 Hash160(const std::vector& vch) { uint256 hash1; diff --git a/src/init.cpp b/src/init.cpp deleted file mode 100644 index 7a1bc323..00000000 --- a/src/init.cpp +++ /dev/null @@ -1,1096 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2011-2013 PPCoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "txdb.h" -#include "walletdb.h" -#include "bitcoinrpc.h" -#include "net.h" -#include "init.h" -#include "util.h" -#include "ui_interface.h" -#include "checkpointsync.h" - -#include -#include -#include -#include -#include -#include - -#ifndef WIN32 -#include -#endif - -using namespace std; -using namespace boost; - -CWallet* pwalletMain; -CClientUIInterface uiInterface; - -#ifdef WIN32 -// Win32 LevelDB doesn't use filedescriptors, and the ones used for -// accessing block files, don't count towards to fd_set size limit -// anyway. -#define MIN_CORE_FILEDESCRIPTORS 0 -#else -#define MIN_CORE_FILEDESCRIPTORS 150 -#endif - -// Used to pass flags to the Bind() function -enum BindFlags { - BF_NONE = 0, - BF_EXPLICIT = (1U << 0), - BF_REPORT_ERROR = (1U << 1) -}; - -////////////////////////////////////////////////////////////////////////////// -// -// Shutdown -// - -// -// Thread management and startup/shutdown: -// -// The network-processing threads are all part of a thread group -// created by AppInit() or the Qt main() function. -// -// A clean exit happens when StartShutdown() or the SIGTERM -// signal handler sets fRequestShutdown, which triggers -// the DetectShutdownThread(), which interrupts the main thread group. -// DetectShutdownThread() then exits, which causes AppInit() to -// continue (it .joins the shutdown thread). -// Shutdown() is then -// called to clean up database connections, and stop other -// threads that should only be stopped after the main network-processing -// threads have exited. -// -// Note that if running -daemon the parent process returns from AppInit2 -// before adding any threads to the threadGroup, so .join_all() returns -// immediately and the parent exits from main(). -// -// Shutdown for Qt is very similar, only it uses a QTimer to detect -// fRequestShutdown getting set, and then does the normal Qt -// shutdown thing. -// - -volatile bool fRequestShutdown = false; - -void StartShutdown() -{ - fRequestShutdown = true; -} -bool ShutdownRequested() -{ - return fRequestShutdown; -} - -static CCoinsViewDB *pcoinsdbview; - -void Shutdown() -{ - static CCriticalSection cs_Shutdown; - TRY_LOCK(cs_Shutdown, lockShutdown); - if (!lockShutdown) return; - - RenameThread("primecoin-shutoff"); - nTransactionsUpdated++; - StopRPCThreads(); - bitdb.Flush(false); - StopNode(); - { - LOCK(cs_main); - if (pwalletMain) - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); - if (pblocktree) - pblocktree->Flush(); - if (pcoinsTip) - pcoinsTip->Flush(); - delete pcoinsTip; pcoinsTip = NULL; - delete pcoinsdbview; pcoinsdbview = NULL; - delete pblocktree; pblocktree = NULL; - } - bitdb.Flush(true); - boost::filesystem::remove(GetPidFile()); - UnregisterWallet(pwalletMain); - delete pwalletMain; -} - -// -// Signal handlers are very limited in what they are allowed to do, so: -// -void DetectShutdownThread(boost::thread_group* threadGroup) -{ - // Tell the main threads to shutdown. - while (!fRequestShutdown) MilliSleep(200); - - GenerateBitcoins(false, NULL); - threadGroup->interrupt_all(); - -} - -void HandleSIGTERM(int) -{ - fRequestShutdown = true; -} - -void HandleSIGHUP(int) -{ - fReopenDebugLog = true; -} - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Start -// -#if !defined(QT_GUI) -bool AppInit(int argc, char* argv[]) -{ - boost::thread_group threadGroup; - boost::thread* detectShutdownThread = NULL; - - bool fRet = false; - try - { - // - // Parameters - // - // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() - ParseParameters(argc, argv); - if (!boost::filesystem::is_directory(GetDataDir(false))) - { - fprintf(stderr, "Error: Specified directory does not exist\n"); - Shutdown(); - } - ReadConfigFile(mapArgs, mapMultiArgs); - - if (mapArgs.count("-?") || mapArgs.count("--help")) - { - // First part of help message is specific to bitcoind / RPC client - std::string strUsage = _("Primecoin version") + " " + FormatFullVersion() + "\n\n" + - _("Usage:") + "\n" + - " primecoind [options] " + "\n" + - " primecoind [options] [params] " + _("Send command to -server or primecoind") + "\n" + - " primecoind [options] help " + _("List commands") + "\n" + - " primecoind [options] help " + _("Get help for a command") + "\n"; - - strUsage += "\n" + HelpMessage(); - - fprintf(stdout, "%s", strUsage.c_str()); - return false; - } - - // Command-line RPC - for (int i = 1; i < argc; i++) - if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "primecoin:")) - fCommandLine = true; - - if (fCommandLine) - { - int ret = CommandLineRPC(argc, argv); - exit(ret); - } -#if !defined(WIN32) - fDaemon = GetBoolArg("-daemon"); - if (fDaemon) - { - // Daemonize - pid_t pid = fork(); - if (pid < 0) - { - fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); - return false; - } - if (pid > 0) // Parent process, pid is child process id - { - CreatePidFile(GetPidFile(), pid); - return true; - } - // Child process falls through to rest of initialization - - pid_t sid = setsid(); - if (sid < 0) - fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); - } -#endif - - detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); - fRet = AppInit2(threadGroup); - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "AppInit()"); - } catch (...) { - PrintExceptionContinue(NULL, "AppInit()"); - } - if (!fRet) { - if (detectShutdownThread) - detectShutdownThread->interrupt(); - threadGroup.interrupt_all(); - } - - if (detectShutdownThread) - { - detectShutdownThread->join(); - delete detectShutdownThread; - detectShutdownThread = NULL; - } - Shutdown(); - - return fRet; -} - -extern void noui_connect(); -int main(int argc, char* argv[]) -{ - bool fRet = false; - - // Connect bitcoind signal handlers - noui_connect(); - - fRet = AppInit(argc, argv); - - if (fRet && fDaemon) - return 0; - - return (fRet ? 0 : 1); -} -#endif - -bool static InitError(const std::string &str) -{ - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); - return false; -} - -bool static InitWarning(const std::string &str) -{ - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); - return true; -} - -bool static Bind(const CService &addr, unsigned int flags) { - if (!(flags & BF_EXPLICIT) && IsLimited(addr)) - return false; - std::string strError; - if (!BindListenPort(addr, strError)) { - if (flags & BF_REPORT_ERROR) - return InitError(strError); - return false; - } - return true; -} - -// Core-specific options shared between UI and daemon -std::string HelpMessage() -{ - string strUsage = _("Options:") + "\n" + - " -? " + _("This help message") + "\n" + - " -conf= " + _("Specify configuration file (default: primecoin.conf)") + "\n" + - " -pid= " + _("Specify pid file (default: primecoind.pid)") + "\n" + - " -gen " + _("Generate coins (default: 0)") + "\n" + - " -datadir= " + _("Specify data directory") + "\n" + - " -dbcache= " + _("Set database cache size in megabytes (default: 25)") + "\n" + - " -timeout= " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + - " -proxy= " + _("Connect through socks proxy") + "\n" + - " -socks= " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + - " -tor= " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n" - " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + - " -port= " + _("Listen for connections on (default: 9911 or testnet: 9913)") + "\n" + - " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + - " -addnode= " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + - " -connect= " + _("Connect only to the specified node(s)") + "\n" + - " -seednode= " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + - " -externalip= " + _("Specify your own public address") + "\n" + - " -onlynet= " + _("Only connect to nodes in network (IPv4, IPv6 or Tor)") + "\n" + - " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + - " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n" + - " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + - " -bind= " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" + - " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" + - " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + - " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + - " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 5000)") + "\n" + - " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 1000)") + "\n" + -#ifdef USE_UPNP -#if USE_UPNP - " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n" + -#else - " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + -#endif -#endif - " -paytxfee= " + _("Fee per KB to add to transactions you send (minimum 1 cent)") + "\n" + -#ifdef QT_GUI - " -server " + _("Accept command line and JSON-RPC commands") + "\n" + -#endif -#if !defined(WIN32) && !defined(QT_GUI) - " -daemon " + _("Run in the background as a daemon and accept commands") + "\n" + -#endif - " -testnet " + _("Use the test network") + "\n" + - " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n" + - " -debugnet " + _("Output extra network debugging information") + "\n" + - " -logtimestamps " + _("Prepend debug output with timestamp") + "\n" + - " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n" + - " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n" + -#ifdef WIN32 - " -printtodebugger " + _("Send trace/debug info to debugger") + "\n" + -#endif - " -rpcuser= " + _("Username for JSON-RPC connections") + "\n" + - " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n" + - " -rpcport= " + _("Listen for JSON-RPC connections on (default: 9912 or testnet: 9914)") + "\n" + - " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + -#ifndef QT_GUI - " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + -#endif - " -rpcthreads= " + _("Set the number of threads to service RPC calls (default: 4)") + "\n" + - " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + - " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + - " -alertnotify= " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" + - " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + - " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + - " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + - " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + - " -checkblocks= " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n" + - " -checklevel= " + _("How thorough the block verification is (0-4, default: 3)") + "\n" + - " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n" + - " -loadblock= " + _("Imports blocks from external blk000??.dat file") + "\n" + - " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n" + - " -par= " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n" + - - "\n" + _("Block creation options:") + "\n" + - " -blockminsize= " + _("Set minimum block size in bytes (default: 0)") + "\n" + - " -blockmaxsize= " + _("Set maximum block size in bytes (default: 250000)") + "\n" + - " -blockprioritysize= " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n" + - - "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n" + - " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + - " -rpcsslcertificatechainfile= " + _("Server certificate file (default: server.cert)") + "\n" + - " -rpcsslprivatekeyfile= " + _("Server private key (default: server.pem)") + "\n" + - " -rpcsslciphers= " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; - - return strUsage; -} - -struct CImportingNow -{ - CImportingNow() { - assert(fImporting == false); - fImporting = true; - } - - ~CImportingNow() { - assert(fImporting == true); - fImporting = false; - } -}; - -void ThreadImport(std::vector vImportFiles) -{ - RenameThread("primecoin-loadblk"); - - // -reindex - if (fReindex) { - CImportingNow imp; - int nFile = 0; - while (true) { - CDiskBlockPos pos(nFile, 0); - FILE *file = OpenBlockFile(pos, true); - if (!file) - break; - printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); - LoadExternalBlockFile(file, &pos); - nFile++; - } - pblocktree->WriteReindexing(false); - fReindex = false; - printf("Reindexing finished\n"); - // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): - InitBlockIndex(); - } - - // hardcoded $DATADIR/bootstrap.dat - filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; - if (filesystem::exists(pathBootstrap)) { - FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); - if (file) { - CImportingNow imp; - filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; - printf("Importing bootstrap.dat...\n"); - LoadExternalBlockFile(file); - RenameOver(pathBootstrap, pathBootstrapOld); - } - } - - // -loadblock= - BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) { - FILE *file = fopen(path.string().c_str(), "rb"); - if (file) { - CImportingNow imp; - printf("Importing %s...\n", path.string().c_str()); - LoadExternalBlockFile(file); - } - } -} - -/** Initialize bitcoin. - * @pre Parameters should be parsed and config file should be read. - */ -bool AppInit2(boost::thread_group& threadGroup) -{ - // ********************************************************* Step 1: setup -#ifdef _MSC_VER - // Turn off Microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif -#if _MSC_VER >= 1400 - // Disable confusing "helpful" text message on abort, Ctrl-C - _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); -#endif -#ifdef WIN32 - // Enable Data Execution Prevention (DEP) - // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 - // A failure is non-critical and needs no further attention! -#ifndef PROCESS_DEP_ENABLE -// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), -// which is not correct. Can be removed, when GCCs winbase.h is fixed! -#define PROCESS_DEP_ENABLE 0x00000001 -#endif - typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); - PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); - if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); - - // Initialize Windows Sockets - WSADATA wsadata; - int ret = WSAStartup(MAKEWORD(2,2), &wsadata); - if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2) - { - return InitError(strprintf("Error: Winsock library failed to start (WSAStartup returned error %d)", ret)); - } -#endif -#ifndef WIN32 - umask(077); - - // Clean shutdown on SIGTERM - struct sigaction sa; - sa.sa_handler = HandleSIGTERM; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - // Reopen debug.log on SIGHUP - struct sigaction sa_hup; - sa_hup.sa_handler = HandleSIGHUP; - sigemptyset(&sa_hup.sa_mask); - sa_hup.sa_flags = 0; - sigaction(SIGHUP, &sa_hup, NULL); -#endif - - // ********************************************************* Step 2: parameter interactions - - fTestNet = GetBoolArg("-testnet"); - - if (mapArgs.count("-bind")) { - // when specifying an explicit binding address, you want to listen on it - // even when -connect or -proxy is specified - SoftSetBoolArg("-listen", true); - } - - if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { - // when only connecting to trusted nodes, do not seed via DNS, or listen by default - SoftSetBoolArg("-dnsseed", false); - SoftSetBoolArg("-listen", false); - } - - if (mapArgs.count("-proxy")) { - // to protect privacy, do not listen by default if a proxy server is specified - SoftSetBoolArg("-listen", false); - } - - if (!GetBoolArg("-listen", true)) { - // do not map ports or try to retrieve public IP when not listening (pointless) - SoftSetBoolArg("-upnp", false); - SoftSetBoolArg("-discover", false); - } - - if (mapArgs.count("-externalip")) { - // if an explicit public IP is specified, do not try to find others - SoftSetBoolArg("-discover", false); - } - - if (GetBoolArg("-salvagewallet")) { - // Rewrite just private keys: rescan to find transactions - SoftSetBoolArg("-rescan", true); - } - - // Make sure enough file descriptors are available - int nBind = std::max((int)mapArgs.count("-bind"), 1); - nMaxConnections = GetArg("-maxconnections", 125); - nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS), 0); - int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); - if (nFD < MIN_CORE_FILEDESCRIPTORS) - return InitError(_("Not enough file descriptors available.")); - if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) - nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; - - // ********************************************************* Step 3: parameter-to-internal-flags - - fDebug = GetBoolArg("-debug"); - fBenchmark = GetBoolArg("-benchmark"); - - // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency - nScriptCheckThreads = GetArg("-par", 0); - if (nScriptCheckThreads <= 0) - nScriptCheckThreads += boost::thread::hardware_concurrency(); - if (nScriptCheckThreads <= 1) - nScriptCheckThreads = 0; - else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) - nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - - // -debug implies fDebug* - if (fDebug) - fDebugNet = true; - else - fDebugNet = GetBoolArg("-debugnet"); - - if (fDaemon) - fServer = true; - else - fServer = GetBoolArg("-server"); - - /* force fServer when running without GUI */ -#if !defined(QT_GUI) - fServer = true; -#endif - fPrintToConsole = GetBoolArg("-printtoconsole"); - fPrintToDebugger = GetBoolArg("-printtodebugger"); - fLogTimestamps = GetBoolArg("-logtimestamps"); - - if (mapArgs.count("-timeout")) - { - int nNewTimeout = GetArg("-timeout", 5000); - if (nNewTimeout > 0 && nNewTimeout < 600000) - nConnectTimeout = nNewTimeout; - } - - // Continue to put "/P2SH/" in the coinbase to monitor - // BIP16 support. - // This can be removed eventually... - const char* pszP2SH = "/P2SH/"; - COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); - - // Fee-per-kilobyte amount considered the same as "free" - // If you are mining, be careful setting this: - // if you set it to zero then - // a transaction spammer can cheaply fill blocks using - // 1-satoshi-fee transactions. It should be set above the real - // cost to you of processing a transaction. - // - // primecoin: -mintxfee and -minrelaytxfee options of bitcoin disabled - // fixed min fees defined in MIN_TX_FEE and MIN_RELAY_TX_FEE - - if (mapArgs.count("-paytxfee")) - { - if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee) || nTransactionFee < CTransaction::nMinTxFee) - return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"].c_str())); - if (nTransactionFee > 0.25 * COIN) - InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); - } - - if (mapArgs.count("-checkpointkey")) // ppcoin: checkpoint master priv key - { - if (!SetCheckpointPrivKey(GetArg("-checkpointkey", ""))) - return InitError(_("Unable to sign checkpoint, wrong checkpointkey?")); - } - - // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log - - std::string strDataDir = GetDataDir().string(); - - // Make sure only a single Bitcoin process is using the data directory. - boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; - FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. - if (file) fclose(file); - static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); - if (!lock.try_lock()) - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Primecoin is probably already running."), strDataDir.c_str())); - - if (GetBoolArg("-shrinkdebugfile", !fDebug)) - ShrinkDebugFile(); - printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - printf("Primecoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); - printf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); - if (!fLogTimestamps) - printf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); - printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); - printf("Using data directory %s\n", strDataDir.c_str()); - printf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); - std::ostringstream strErrors; - - if (fDaemon) - fprintf(stdout, "Primecoin server starting\n"); - - if (nScriptCheckThreads) { - printf("Using %u threads for script verification\n", nScriptCheckThreads); - for (int i=0; i nets; - BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) { - enum Network net = ParseNetwork(snet); - if (net == NET_UNROUTABLE) - return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet.c_str())); - nets.insert(net); - } - for (int n = 0; n < NET_MAX; n++) { - enum Network net = (enum Network)n; - if (!nets.count(net)) - SetLimited(net); - } - } -#if defined(USE_IPV6) -#if ! USE_IPV6 - else - SetLimited(NET_IPV6); -#endif -#endif - - CService addrProxy; - bool fProxy = false; - if (mapArgs.count("-proxy")) { - addrProxy = CService(mapArgs["-proxy"], 9050); - if (!addrProxy.IsValid()) - return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str())); - - if (!IsLimited(NET_IPV4)) - SetProxy(NET_IPV4, addrProxy, nSocksVersion); - if (nSocksVersion > 4) { -#ifdef USE_IPV6 - if (!IsLimited(NET_IPV6)) - SetProxy(NET_IPV6, addrProxy, nSocksVersion); -#endif - SetNameProxy(addrProxy, nSocksVersion); - } - fProxy = true; - } - - // -tor can override normal proxy, -notor disables tor entirely - if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { - CService addrOnion; - if (!mapArgs.count("-tor")) - addrOnion = addrProxy; - else - addrOnion = CService(mapArgs["-tor"], 9050); - if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str())); - SetProxy(NET_TOR, addrOnion, 5); - SetReachable(NET_TOR); - } - - // see Step 2: parameter interactions for more information about these - fNoListen = !GetBoolArg("-listen", true); - fDiscover = GetBoolArg("-discover", true); - fNameLookup = GetBoolArg("-dns", true); - - bool fBound = false; - if (!fNoListen) { - if (mapArgs.count("-bind")) { - BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { - CService addrBind; - if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) - return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); - fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); - } - } - else { - struct in_addr inaddr_any; - inaddr_any.s_addr = INADDR_ANY; -#ifdef USE_IPV6 - fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); -#endif - fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); - } - if (!fBound) - return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); - } - - if (mapArgs.count("-externalip")) { - BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { - CService addrLocal(strAddr, GetListenPort(), fNameLookup); - if (!addrLocal.IsValid()) - return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str())); - AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); - } - } - - BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) - AddOneShot(strDest); - - // ********************************************************* Step 7: load block chain - - fReindex = GetBoolArg("-reindex"); - - // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ - filesystem::path blocksDir = GetDataDir() / "blocks"; - if (!filesystem::exists(blocksDir)) - { - filesystem::create_directories(blocksDir); - bool linked = false; - for (unsigned int i = 1; i < 10000; i++) { - filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); - if (!filesystem::exists(source)) break; - filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); - try { - filesystem::create_hard_link(source, dest); - printf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str()); - linked = true; - } catch (filesystem::filesystem_error & e) { - // Note: hardlink creation failing is not a disaster, it just means - // blocks will get re-downloaded from peers. - printf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); - break; - } - } - if (linked) - { - fReindex = true; - } - } - - // cache size calculations - size_t nTotalCache = GetArg("-dbcache", 25) << 20; - if (nTotalCache < (1 << 22)) - nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB - size_t nBlockTreeDBCache = nTotalCache / 8; - if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) - nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB - nTotalCache -= nBlockTreeDBCache; - size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache - nTotalCache -= nCoinDBCache; - nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes - - bool fLoaded = false; - while (!fLoaded) { - bool fReset = fReindex; - std::string strLoadError; - - uiInterface.InitMessage(_("Loading block index...")); - - nStart = GetTimeMillis(); - do { - try { - UnloadBlockIndex(); - delete pcoinsTip; - delete pcoinsdbview; - delete pblocktree; - - pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); - pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); - pcoinsTip = new CCoinsViewCache(*pcoinsdbview); - - if (fReindex) - pblocktree->WriteReindexing(true); - - if (!LoadBlockIndex()) { - strLoadError = _("Error loading block database"); - break; - } - - // Initialize the block index (no-op if non-empty database was already loaded) - if (!InitBlockIndex()) { - strLoadError = _("Error initializing block database"); - break; - } - - uiInterface.InitMessage(_("Verifying blocks...")); - if (!VerifyDB()) { - strLoadError = _("Corrupted block database detected"); - break; - } - } catch(std::exception &e) { - strLoadError = _("Error opening block database"); - break; - } - - fLoaded = true; - } while(false); - - if (!fLoaded) { - // first suggest a reindex - if (!fReset) { - bool fRet = uiInterface.ThreadSafeMessageBox( - strLoadError + ".\n" + _("Do you want to rebuild the block database now?"), - "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); - if (fRet) { - fReindex = true; - fRequestShutdown = false; - } else { - return false; - } - } else { - return InitError(strLoadError); - } - } - } - - if (mapArgs.count("-txindex") && fTxIndex != GetBoolArg("-txindex", false)) - return InitError(_("You need to rebuild the databases using -reindex to change -txindex")); - - // as LoadBlockIndex can take several minutes, it's possible the user - // requested to kill bitcoin-qt during the last operation. If so, exit. - // As the program has not fully started yet, Shutdown() is possibly overkill. - if (fRequestShutdown) - { - printf("Shutdown requested. Exiting.\n"); - return false; - } - printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); - - if (mapArgs.count("-printblock")) - { - string strMatch = mapArgs["-printblock"]; - int nFound = 0; - for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) - { - uint256 hash = (*mi).first; - if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) - { - CBlockIndex* pindex = (*mi).second; - CBlock block; - block.ReadFromDisk(pindex); - block.BuildMerkleTree(); - block.print(); - printf("\n"); - nFound++; - } - } - if (nFound == 0) - printf("No blocks matching %s were found\n", strMatch.c_str()); - return false; - } - - // ********************************************************* Step 8: load wallet - - uiInterface.InitMessage(_("Loading wallet...")); - - nStart = GetTimeMillis(); - bool fFirstRun = true; - pwalletMain = new CWallet("wallet.dat"); - DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); - if (nLoadWalletRet != DB_LOAD_OK) - { - if (nLoadWalletRet == DB_CORRUPT) - strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; - else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) - { - string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" - " or address book entries might be missing or incorrect.")); - InitWarning(msg); - } - else if (nLoadWalletRet == DB_TOO_NEW) - strErrors << _("Error loading wallet.dat: Wallet requires newer version of Primecoin") << "\n"; - else if (nLoadWalletRet == DB_NEED_REWRITE) - { - strErrors << _("Wallet needed to be rewritten: restart Primecoin to complete") << "\n"; - printf("%s", strErrors.str().c_str()); - return InitError(strErrors.str()); - } - else - strErrors << _("Error loading wallet.dat") << "\n"; - } - - if (GetBoolArg("-upgradewallet", fFirstRun)) - { - int nMaxVersion = GetArg("-upgradewallet", 0); - if (nMaxVersion == 0) // the -upgradewallet without argument case - { - printf("Performing wallet upgrade to %i\n", FEATURE_LATEST); - nMaxVersion = CLIENT_VERSION; - pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately - } - else - printf("Allowing wallet upgrade up to %i\n", nMaxVersion); - if (nMaxVersion < pwalletMain->GetVersion()) - strErrors << _("Cannot downgrade wallet") << "\n"; - pwalletMain->SetMaxVersion(nMaxVersion); - } - - if (fFirstRun) - { - // Create new keyUser and set as default key - RandAddSeedPerfmon(); - - CPubKey newDefaultKey; - if (pwalletMain->GetKeyFromPool(newDefaultKey, false)) { - pwalletMain->SetDefaultKey(newDefaultKey); - if (!pwalletMain->SetAddressBookName(pwalletMain->vchDefaultKey.GetID(), "")) - strErrors << _("Cannot write default address") << "\n"; - } - - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); - } - - printf("%s", strErrors.str().c_str()); - printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); - - RegisterWallet(pwalletMain); - - if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) - { - PrintBlockTree(); - return false; - } - - CBlockIndex *pindexRescan = pindexBest; - if (GetBoolArg("-rescan")) - pindexRescan = pindexGenesisBlock; - else - { - CWalletDB walletdb("wallet.dat"); - CBlockLocator locator; - if (walletdb.ReadBestBlock(locator)) - pindexRescan = locator.GetBlockIndex(); - else - pindexRescan = pindexGenesisBlock; - } - if (pindexBest && pindexBest != pindexRescan) - { - uiInterface.InitMessage(_("Rescanning...")); - printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); - nStart = GetTimeMillis(); - pwalletMain->ScanForWalletTransactions(pindexRescan, true); - printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); - nWalletDBUpdated++; - } - - // ********************************************************* Step 9: import blocks - - // scan for better chains in the block chain database, that are not yet connected in the active best chain - CValidationState state; - if (!ConnectBestBlock(state)) - strErrors << "Failed to connect best block"; - - std::vector vImportFiles; - if (mapArgs.count("-loadblock")) - { - BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) - vImportFiles.push_back(strFile); - } - threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); - - // ********************************************************* Step 10: load peers - - uiInterface.InitMessage(_("Loading addresses...")); - - nStart = GetTimeMillis(); - - { - CAddrDB adb; - if (!adb.Read(addrman)) - printf("Invalid or missing peers.dat; recreating\n"); - } - - printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", - addrman.size(), GetTimeMillis() - nStart); - - // ********************************************************* Step 11: start node - - if (!CheckDiskSpace()) - return false; - - if (!strErrors.str().empty()) - return InitError(strErrors.str()); - - RandAddSeedPerfmon(); - - //// debug print - printf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); - printf("nBestHeight = %d\n", nBestHeight); - printf("setKeyPool.size() = %"PRIszu"\n", pwalletMain->setKeyPool.size()); - printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); - printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); - - StartNode(threadGroup); - - if (fServer) - StartRPCThreads(); - - // Generate coins in the background - GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); - - // ********************************************************* Step 12: finished - - uiInterface.InitMessage(_("Done loading")); - - // Add wallet transactions that aren't already in a block to mapTransactions - pwalletMain->ReacceptWalletTransactions(); - - // Run a thread to flush wallet periodically - threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); - - return !fRequestShutdown; -} diff --git a/src/init.h b/src/init.h deleted file mode 100644 index 5927670c..00000000 --- a/src/init.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_INIT_H -#define BITCOIN_INIT_H - -#include "wallet.h" - -extern CWallet* pwalletMain; - -void StartShutdown(); -bool ShutdownRequested(); -void Shutdown(); -bool AppInit2(boost::thread_group& threadGroup); -std::string HelpMessage(); - -#endif diff --git a/src/key.cpp b/src/key.cpp deleted file mode 100644 index 20114e6b..00000000 --- a/src/key.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include - -#include -#include - -#include "key.h" - -// Generate a private key from just the secret parameter -int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) -{ - int ok = 0; - BN_CTX *ctx = NULL; - EC_POINT *pub_key = NULL; - - if (!eckey) return 0; - - const EC_GROUP *group = EC_KEY_get0_group(eckey); - - if ((ctx = BN_CTX_new()) == NULL) - goto err; - - pub_key = EC_POINT_new(group); - - if (pub_key == NULL) - goto err; - - if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) - goto err; - - EC_KEY_set_private_key(eckey,priv_key); - EC_KEY_set_public_key(eckey,pub_key); - - ok = 1; - -err: - - if (pub_key) - EC_POINT_free(pub_key); - if (ctx != NULL) - BN_CTX_free(ctx); - - return(ok); -} - -// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields -// recid selects which key is recovered -// if check is non-zero, additional checks are performed -int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) -{ - if (!eckey) return 0; - - int ret = 0; - BN_CTX *ctx = NULL; - - BIGNUM *x = NULL; - BIGNUM *e = NULL; - BIGNUM *order = NULL; - BIGNUM *sor = NULL; - BIGNUM *eor = NULL; - BIGNUM *field = NULL; - EC_POINT *R = NULL; - EC_POINT *O = NULL; - EC_POINT *Q = NULL; - BIGNUM *rr = NULL; - BIGNUM *zero = NULL; - int n = 0; - int i = recid / 2; - - const EC_GROUP *group = EC_KEY_get0_group(eckey); - if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } - BN_CTX_start(ctx); - order = BN_CTX_get(ctx); - if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } - x = BN_CTX_get(ctx); - if (!BN_copy(x, order)) { ret=-1; goto err; } - if (!BN_mul_word(x, i)) { ret=-1; goto err; } - if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } - field = BN_CTX_get(ctx); - if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } - if (BN_cmp(x, field) >= 0) { ret=0; goto err; } - if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } - if (check) - { - if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } - if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } - } - if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - n = EC_GROUP_get_degree(group); - e = BN_CTX_get(ctx); - if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } - if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); - zero = BN_CTX_get(ctx); - if (!BN_zero(zero)) { ret=-1; goto err; } - if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } - rr = BN_CTX_get(ctx); - if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } - sor = BN_CTX_get(ctx); - if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } - eor = BN_CTX_get(ctx); - if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } - if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } - if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } - - ret = 1; - -err: - if (ctx) { - BN_CTX_end(ctx); - BN_CTX_free(ctx); - } - if (R != NULL) EC_POINT_free(R); - if (O != NULL) EC_POINT_free(O); - if (Q != NULL) EC_POINT_free(Q); - return ret; -} - -void CKey::SetCompressedPubKey(bool fCompressed) -{ - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - fCompressedPubKey = true; -} - -void CKey::Reset() -{ - fCompressedPubKey = false; - if (pkey != NULL) - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) - throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); - fSet = false; -} - -CKey::CKey() -{ - pkey = NULL; - Reset(); -} - -CKey::CKey(const CKey& b) -{ - pkey = EC_KEY_dup(b.pkey); - if (pkey == NULL) - throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); - fSet = b.fSet; -} - -CKey& CKey::operator=(const CKey& b) -{ - if (!EC_KEY_copy(pkey, b.pkey)) - throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); - fSet = b.fSet; - return (*this); -} - -CKey::~CKey() -{ - EC_KEY_free(pkey); -} - -bool CKey::IsNull() const -{ - return !fSet; -} - -bool CKey::IsCompressed() const -{ - return fCompressedPubKey; -} - -void CKey::MakeNewKey(bool fCompressed) -{ - if (!EC_KEY_generate_key(pkey)) - throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); - if (fCompressed) - SetCompressedPubKey(); - fSet = true; -} - -bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) -{ - const unsigned char* pbegin = &vchPrivKey[0]; - if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) - { - // In testing, d2i_ECPrivateKey can return true - // but fill in pkey with a key that fails - // EC_KEY_check_key, so: - if (EC_KEY_check_key(pkey)) - { - fSet = true; - return true; - } - } - // If vchPrivKey data is bad d2i_ECPrivateKey() can - // leave pkey in a state where calling EC_KEY_free() - // crashes. To avoid that, set pkey to NULL and - // leak the memory (a leak is better than a crash) - pkey = NULL; - Reset(); - return false; -} - -bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) -{ - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) - throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed"); - if (vchSecret.size() != 32) - throw key_error("CKey::SetSecret() : secret must be 32 bytes"); - BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); - if (bn == NULL) - throw key_error("CKey::SetSecret() : BN_bin2bn failed"); - if (!EC_KEY_regenerate_key(pkey,bn)) - { - BN_clear_free(bn); - throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); - } - BN_clear_free(bn); - fSet = true; - if (fCompressed || fCompressedPubKey) - SetCompressedPubKey(); - return true; -} - -CSecret CKey::GetSecret(bool &fCompressed) const -{ - CSecret vchRet; - vchRet.resize(32); - const BIGNUM *bn = EC_KEY_get0_private_key(pkey); - int nBytes = BN_num_bytes(bn); - if (bn == NULL) - throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed"); - int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); - if (n != nBytes) - throw key_error("CKey::GetSecret(): BN_bn2bin failed"); - fCompressed = fCompressedPubKey; - return vchRet; -} - -CPrivKey CKey::GetPrivKey() const -{ - int nSize = i2d_ECPrivateKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); - CPrivKey vchPrivKey(nSize, 0); - unsigned char* pbegin = &vchPrivKey[0]; - if (i2d_ECPrivateKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size"); - return vchPrivKey; -} - -bool CKey::SetPubKey(const CPubKey& vchPubKey) -{ - const unsigned char* pbegin = &vchPubKey.vchPubKey[0]; - if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) - { - fSet = true; - if (vchPubKey.vchPubKey.size() == 33) - SetCompressedPubKey(); - return true; - } - pkey = NULL; - Reset(); - return false; -} - -CPubKey CKey::GetPubKey() const -{ - int nSize = i2o_ECPublicKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); - std::vector vchPubKey(nSize, 0); - unsigned char* pbegin = &vchPubKey[0]; - if (i2o_ECPublicKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); - return CPubKey(vchPubKey); -} - -bool CKey::Sign(uint256 hash, std::vector& vchSig) -{ - unsigned int nSize = ECDSA_size(pkey); - vchSig.resize(nSize); // Make sure it is big enough - if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey)) - { - vchSig.clear(); - return false; - } - vchSig.resize(nSize); // Shrink to fit actual size - return true; -} - -// create a compact signature (65 bytes), which allows reconstructing the used public key -// The format is one header byte, followed by two times 32 bytes for the serialized r and s values. -// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, -// 0x1D = second key with even y, 0x1E = second key with odd y -bool CKey::SignCompact(uint256 hash, std::vector& vchSig) -{ - bool fOk = false; - ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig==NULL) - return false; - vchSig.clear(); - vchSig.resize(65,0); - int nBitsR = BN_num_bits(sig->r); - int nBitsS = BN_num_bits(sig->s); - if (nBitsR <= 256 && nBitsS <= 256) - { - int nRecId = -1; - for (int i=0; i<4; i++) - { - CKey keyRec; - keyRec.fSet = true; - if (fCompressedPubKey) - keyRec.SetCompressedPubKey(); - if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) - if (keyRec.GetPubKey() == this->GetPubKey()) - { - nRecId = i; - break; - } - } - - if (nRecId == -1) - throw key_error("CKey::SignCompact() : unable to construct recoverable key"); - - vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); - BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); - BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]); - fOk = true; - } - ECDSA_SIG_free(sig); - return fOk; -} - -// reconstruct public key from a compact signature -// This is only slightly more CPU intensive than just verifying it. -// If this function succeeds, the recovered public key is guaranteed to be valid -// (the signature is a valid signature of the given data for that key) -bool CKey::SetCompactSignature(uint256 hash, const std::vector& vchSig) -{ - if (vchSig.size() != 65) - return false; - int nV = vchSig[0]; - if (nV<27 || nV>=35) - return false; - ECDSA_SIG *sig = ECDSA_SIG_new(); - BN_bin2bn(&vchSig[1],32,sig->r); - BN_bin2bn(&vchSig[33],32,sig->s); - - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (nV >= 31) - { - SetCompressedPubKey(); - nV -= 4; - } - if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1) - { - fSet = true; - ECDSA_SIG_free(sig); - return true; - } - return false; -} - -bool CKey::Verify(uint256 hash, const std::vector& vchSig) -{ - // -1 = error, 0 = bad sig, 1 = good - if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) - return false; - - return true; -} - -bool CKey::VerifyCompact(uint256 hash, const std::vector& vchSig) -{ - CKey key; - if (!key.SetCompactSignature(hash, vchSig)) - return false; - if (GetPubKey() != key.GetPubKey()) - return false; - - return true; -} - -bool CKey::IsValid() -{ - if (!fSet) - return false; - - if (!EC_KEY_check_key(pkey)) - return false; - - bool fCompr; - CSecret secret = GetSecret(fCompr); - CKey key2; - key2.SetSecret(secret, fCompr); - return GetPubKey() == key2.GetPubKey(); -} diff --git a/src/keystore.cpp b/src/keystore.cpp deleted file mode 100644 index e0cf805a..00000000 --- a/src/keystore.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "keystore.h" -#include "script.h" - -bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const -{ - CKey key; - if (!GetKey(address, key)) - return false; - vchPubKeyOut = key.GetPubKey(); - return true; -} - -bool CBasicKeyStore::AddKey(const CKey& key) -{ - bool fCompressed = false; - CSecret secret = key.GetSecret(fCompressed); - { - LOCK(cs_KeyStore); - mapKeys[key.GetPubKey().GetID()] = make_pair(secret, fCompressed); - } - return true; -} - -bool CBasicKeyStore::AddCScript(const CScript& redeemScript) -{ - { - LOCK(cs_KeyStore); - mapScripts[redeemScript.GetID()] = redeemScript; - } - return true; -} - -bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const -{ - bool result; - { - LOCK(cs_KeyStore); - result = (mapScripts.count(hash) > 0); - } - return result; -} - - -bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const -{ - { - LOCK(cs_KeyStore); - ScriptMap::const_iterator mi = mapScripts.find(hash); - if (mi != mapScripts.end()) - { - redeemScriptOut = (*mi).second; - return true; - } - } - return false; -} - -bool CCryptoKeyStore::SetCrypted() -{ - { - LOCK(cs_KeyStore); - if (fUseCrypto) - return true; - if (!mapKeys.empty()) - return false; - fUseCrypto = true; - } - return true; -} - -bool CCryptoKeyStore::Lock() -{ - if (!SetCrypted()) - return false; - - { - LOCK(cs_KeyStore); - vMasterKey.clear(); - } - - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) -{ - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - for (; mi != mapCryptedKeys.end(); ++mi) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector &vchCryptedSecret = (*mi).second.second; - CSecret vchSecret; - if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - CKey key; - key.SetPubKey(vchPubKey); - key.SetSecret(vchSecret); - if (key.GetPubKey() == vchPubKey) - break; - return false; - } - vMasterKey = vMasterKeyIn; - } - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::AddKey(const CKey& key) -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::AddKey(key); - - if (IsLocked()) - return false; - - std::vector vchCryptedSecret; - CPubKey vchPubKey = key.GetPubKey(); - bool fCompressed; - if (!EncryptSecret(vMasterKey, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) - return false; - - if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) - return false; - } - return true; -} - - -bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) -{ - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); - } - return true; -} - -bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetKey(address, keyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector &vchCryptedSecret = (*mi).second.second; - CSecret vchSecret; - if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - keyOut.SetPubKey(vchPubKey); - keyOut.SetSecret(vchSecret); - return true; - } - } - return false; -} - -bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CKeyStore::GetPubKey(address, vchPubKeyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - vchPubKeyOut = (*mi).second.first; - return true; - } - } - return false; -} - -bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) -{ - { - LOCK(cs_KeyStore); - if (!mapCryptedKeys.empty() || IsCrypted()) - return false; - - fUseCrypto = true; - BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) - { - CKey key; - if (!key.SetSecret(mKey.second.first, mKey.second.second)) - return false; - const CPubKey vchPubKey = key.GetPubKey(); - std::vector vchCryptedSecret; - bool fCompressed; - if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) - return false; - if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) - return false; - } - mapKeys.clear(); - } - return true; -} diff --git a/src/leveldb.cpp b/src/leveldb.cpp deleted file mode 100644 index e66f8514..00000000 --- a/src/leveldb.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "leveldb.h" -#include "util.h" - -#include -#include -#include -#include - -#include - -void HandleError(const leveldb::Status &status) throw(leveldb_error) { - if (status.ok()) - return; - if (status.IsCorruption()) - throw leveldb_error("Database corrupted"); - if (status.IsIOError()) - throw leveldb_error("Database I/O error"); - if (status.IsNotFound()) - throw leveldb_error("Database entry missing"); - throw leveldb_error("Unknown database error"); -} - -static leveldb::Options GetOptions(size_t nCacheSize) { - leveldb::Options options; - options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); - options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously - options.filter_policy = leveldb::NewBloomFilterPolicy(10); - options.compression = leveldb::kNoCompression; - options.max_open_files = 64; - return options; -} - -CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { - penv = NULL; - readoptions.verify_checksums = true; - iteroptions.verify_checksums = true; - iteroptions.fill_cache = false; - syncoptions.sync = true; - options = GetOptions(nCacheSize); - options.create_if_missing = true; - if (fMemory) { - penv = leveldb::NewMemEnv(leveldb::Env::Default()); - options.env = penv; - } else { - if (fWipe) { - printf("Wiping LevelDB in %s\n", path.string().c_str()); - leveldb::DestroyDB(path.string(), options); - } - boost::filesystem::create_directory(path); - printf("Opening LevelDB in %s\n", path.string().c_str()); - } - leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); - if (!status.ok()) - throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str())); - printf("Opened LevelDB successfully\n"); -} - -CLevelDB::~CLevelDB() { - delete pdb; - pdb = NULL; - delete options.filter_policy; - options.filter_policy = NULL; - delete options.block_cache; - options.block_cache = NULL; - delete penv; - options.env = NULL; -} - -bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { - leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); - if (!status.ok()) { - printf("LevelDB write failure: %s\n", status.ToString().c_str()); - HandleError(status); - return false; - } - return true; -} diff --git a/src/leveldb.h b/src/leveldb.h deleted file mode 100644 index 79262edb..00000000 --- a/src/leveldb.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_LEVELDB_H -#define BITCOIN_LEVELDB_H - -#include "serialize.h" - -#include -#include - -#include - -class leveldb_error : public std::runtime_error -{ -public: - leveldb_error(const std::string &msg) : std::runtime_error(msg) {} -}; - -void HandleError(const leveldb::Status &status) throw(leveldb_error); - -// Batch of changes queued to be written to a CLevelDB -class CLevelDBBatch -{ - friend class CLevelDB; - -private: - leveldb::WriteBatch batch; - -public: - template void Write(const K& key, const V& value) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(ssValue.GetSerializeSize(value)); - ssValue << value; - leveldb::Slice slValue(&ssValue[0], ssValue.size()); - - batch.Put(slKey, slValue); - } - - template void Erase(const K& key) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - batch.Delete(slKey); - } -}; - -class CLevelDB -{ -private: - // custom environment this database is using (may be NULL in case of default environment) - leveldb::Env *penv; - - // database options used - leveldb::Options options; - - // options used when reading from the database - leveldb::ReadOptions readoptions; - - // options used when iterating over values of the database - leveldb::ReadOptions iteroptions; - - // options used when writing to the database - leveldb::WriteOptions writeoptions; - - // options used when sync writing to the database - leveldb::WriteOptions syncoptions; - - // the database itself - leveldb::DB *pdb; - -public: - CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); - ~CLevelDB(); - - template bool Read(const K& key, V& value) throw(leveldb_error) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - std::string strValue; - leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); - if (!status.ok()) { - if (status.IsNotFound()) - return false; - printf("LevelDB read failure: %s\n", status.ToString().c_str()); - HandleError(status); - } - try { - CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); - ssValue >> value; - } catch(std::exception &e) { - return false; - } - return true; - } - - template bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) { - CLevelDBBatch batch; - batch.Write(key, value); - return WriteBatch(batch, fSync); - } - - template bool Exists(const K& key) throw(leveldb_error) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - std::string strValue; - leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); - if (!status.ok()) { - if (status.IsNotFound()) - return false; - printf("LevelDB read failure: %s\n", status.ToString().c_str()); - HandleError(status); - } - return true; - } - - template bool Erase(const K& key, bool fSync = false) throw(leveldb_error) { - CLevelDBBatch batch; - batch.Erase(key); - return WriteBatch(batch, fSync); - } - - bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error); - - // not available for LevelDB; provide for compatibility with BDB - bool Flush() { - return true; - } - - bool Sync() throw(leveldb_error) { - CLevelDBBatch batch; - return WriteBatch(batch, true); - } - - // not exactly clean encapsulation, but it's easiest for now - leveldb::Iterator *NewIterator() { - return pdb->NewIterator(iteroptions); - } -}; - -#endif // BITCOIN_LEVELDB_H diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index d920c4d5..00000000 --- a/src/main.cpp +++ /dev/null @@ -1,5004 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2011-2013 PPCoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#include "alert.h" -#include "checkpoints.h" -#include "db.h" -#include "txdb.h" -#include "net.h" -#include "init.h" -#include "ui_interface.h" -#include "checkqueue.h" -#include "prime.h" -#include "checkpointsync.h" -#include -#include -#include - -using namespace std; -using namespace boost; - -// -// Global state -// - -CCriticalSection cs_setpwalletRegistered; -set setpwalletRegistered; - -CCriticalSection cs_main; - -CTxMemPool mempool; -unsigned int nTransactionsUpdated = 0; - -map mapBlockIndex; -uint256 hashGenesisBlock = hashGenesisBlockOfficial; -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -uint256 nBestChainWork = 0; -uint256 nBestInvalidWork = 0; -uint256 hashBestChain = 0; -CBlockIndex* pindexBest = NULL; -set setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed -int64 nTimeBestReceived = 0; -int nScriptCheckThreads = 0; -bool fImporting = false; -bool fReindex = false; -bool fBenchmark = false; -bool fTxIndex = false; -unsigned int nCoinCacheSize = 5000; - -/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ -int64 CTransaction::nMinTxFee = MIN_TX_FEE; // Override with -mintxfee -/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ -int64 CTransaction::nMinRelayTxFee = MIN_TX_FEE; - -CMedianFilter cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have - -map mapOrphanBlocks; -multimap mapOrphanBlocksByPrev; - -map mapOrphanTransactions; -map > mapOrphanTransactionsByPrev; - -// Constant stuff for coinbase transactions we create: -CScript COINBASE_FLAGS; - -const string strMessageMagic = "Primecoin Signed Message:\n"; - -double dPrimesPerSec = 0.0; -double dChainsPerMinute = 0.0; -double dChainsPerDay = 0.0; -int64 nHPSTimerStart = 0; - -// Settings -int64 nTransactionFee = 0; - - - -////////////////////////////////////////////////////////////////////////////// -// -// dispatching functions -// - -// These functions dispatch to one or all registered wallets - - -void RegisterWallet(CWallet* pwalletIn) -{ - { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.insert(pwalletIn); - } -} - -void UnregisterWallet(CWallet* pwalletIn) -{ - { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.erase(pwalletIn); - } -} - -// get the wallet transaction with the given hash (if it exists) -bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - if (pwallet->GetTransaction(hashTx,wtx)) - return true; - return false; -} - -// erases transaction with the given hash from all wallets -void static EraseFromWallets(uint256 hash) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->EraseFromWallet(hash); -} - -// make sure all wallets know about the given transaction, in the given block -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); -} - -// notify wallets about a new best chain -void static SetBestChain(const CBlockLocator& loc) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->SetBestChain(loc); -} - -// notify wallets about an updated transaction -void static UpdatedTransaction(const uint256& hashTx) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->UpdatedTransaction(hashTx); -} - -// dump all wallets -void static PrintWallets(const CBlock& block) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->PrintWallet(block); -} - -// notify wallets about an incoming inventory (for request counts) -void static Inventory(const uint256& hash) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->Inventory(hash); -} - -// ask wallets to resend their transactions -void static ResendWalletTransactions() -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->ResendWalletTransactions(); -} - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CCoinsView implementations -// - -bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } -bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } -bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } -CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } -bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } -bool CCoinsView::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return false; } -bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } - - -CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } -bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } -bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } -bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } -CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } -bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } -void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } -bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } -bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } - -CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } - -bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { - if (cacheCoins.count(txid)) { - coins = cacheCoins[txid]; - return true; - } - if (base->GetCoins(txid, coins)) { - cacheCoins[txid] = coins; - return true; - } - return false; -} - -std::map::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { - std::map::iterator it = cacheCoins.lower_bound(txid); - if (it != cacheCoins.end() && it->first == txid) - return it; - CCoins tmp; - if (!base->GetCoins(txid,tmp)) - return cacheCoins.end(); - std::map::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); - tmp.swap(ret->second); - return ret; -} - -CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { - std::map::iterator it = FetchCoins(txid); - assert(it != cacheCoins.end()); - return it->second; -} - -bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { - cacheCoins[txid] = coins; - return true; -} - -bool CCoinsViewCache::HaveCoins(const uint256 &txid) { - return FetchCoins(txid) != cacheCoins.end(); -} - -CBlockIndex *CCoinsViewCache::GetBestBlock() { - if (pindexTip == NULL) - pindexTip = base->GetBestBlock(); - return pindexTip; -} - -bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { - pindexTip = pindex; - return true; -} - -bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { - for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) - cacheCoins[it->first] = it->second; - pindexTip = pindex; - return true; -} - -bool CCoinsViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, pindexTip); - if (fOk) - cacheCoins.clear(); - return fOk; -} - -unsigned int CCoinsViewCache::GetCacheSize() { - return cacheCoins.size(); -} - -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ -CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } - -bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { - if (base->GetCoins(txid, coins)) - return true; - if (mempool.exists(txid)) { - const CTransaction &tx = mempool.lookup(txid); - coins = CCoins(tx, MEMPOOL_HEIGHT); - return true; - } - return false; -} - -bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { - return mempool.exists(txid) || base->HaveCoins(txid); -} - -CCoinsViewCache *pcoinsTip = NULL; -CBlockTreeDB *pblocktree = NULL; - -////////////////////////////////////////////////////////////////////////////// -// -// mapOrphanTransactions -// - -bool AddOrphanTx(const CDataStream& vMsg) -{ - CTransaction tx; - CDataStream(vMsg) >> tx; - uint256 hash = tx.GetHash(); - if (mapOrphanTransactions.count(hash)) - return false; - - CDataStream* pvMsg = new CDataStream(vMsg); - - // Ignore big transactions, to avoid a - // send-big-orphans memory exhaustion attack. If a peer has a legitimate - // large transaction with a missing parent then we assume - // it will rebroadcast it later, after the parent transaction(s) - // have been mined or received. - // 10,000 orphans, each of which is at most 5,000 bytes big is - // at most 500 megabytes of orphans: - if (pvMsg->size() > 5000) - { - printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().c_str()); - delete pvMsg; - return false; - } - - mapOrphanTransactions[hash] = pvMsg; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg)); - - printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), - mapOrphanTransactions.size()); - return true; -} - -void static EraseOrphanTx(uint256 hash) -{ - if (!mapOrphanTransactions.count(hash)) - return; - const CDataStream* pvMsg = mapOrphanTransactions[hash]; - CTransaction tx; - CDataStream(*pvMsg) >> tx; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); - if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) - mapOrphanTransactionsByPrev.erase(txin.prevout.hash); - } - delete pvMsg; - mapOrphanTransactions.erase(hash); -} - -unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -{ - unsigned int nEvicted = 0; - while (mapOrphanTransactions.size() > nMaxOrphans) - { - // Evict a random orphan: - uint256 randomhash = GetRandHash(); - map::iterator it = mapOrphanTransactions.lower_bound(randomhash); - if (it == mapOrphanTransactions.end()) - it = mapOrphanTransactions.begin(); - EraseOrphanTx(it->first); - ++nEvicted; - } - return nEvicted; -} - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CTransaction / CTxOut -// - -bool CTxOut::IsDust() const -{ - // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, - // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees - // to spend something, then we consider it dust. - // A typical txout is 33 bytes big, and will - // need a CTxIn of at least 148 bytes to spend, - // so dust is a txout less than 54 uBTC - // (5430 satoshis) with default nMinRelayTxFee - return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < CTransaction::nMinRelayTxFee); -} - -bool CTransaction::IsStandard() const -{ - if (nVersion > CTransaction::CURRENT_VERSION) - return false; - - if (!IsFinal()) - return false; - - // Extremely large transactions with lots of inputs can cost the network - // almost as much to process as they cost the sender in fees, because - // computing signature hashes is O(ninputs*txsize). Limiting transactions - // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) - return false; - - BOOST_FOREACH(const CTxIn& txin, vin) - { - // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG - // pay-to-script-hash, which is 3 ~80-byte signatures, 3 - // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) - return false; - if (!txin.scriptSig.IsPushOnly()) - return false; - } - BOOST_FOREACH(const CTxOut& txout, vout) { - if (!::IsStandard(txout.scriptPubKey)) - return false; - if (txout.IsDust()) - return false; - } - return true; -} - -// -// Check transaction inputs, and make sure any -// pay-to-script-hash transactions are evaluating IsStandard scripts -// -// Why bother? To avoid denial-of-service attacks; an attacker -// can submit a standard HASH... OP_EQUAL transaction, -// which will get accepted into blocks. The redemption -// script can be anything; an attacker could use a very -// expensive-to-check-upon-redemption script like: -// DUP CHECKSIG DROP ... repeated 100 times... OP_1 -// -bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const -{ - if (IsCoinBase()) - return true; // Coinbases don't use vin normally - - for (unsigned int i = 0; i < vin.size(); i++) - { - const CTxOut& prev = GetOutputFor(vin[i], mapInputs); - - vector > vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) - return false; - int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); - if (nArgsExpected < 0) - return false; - - // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig the - // IsStandard() call returns false - vector > stack; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0)) - return false; - - if (whichType == TX_SCRIPTHASH) - { - if (stack.empty()) - return false; - CScript subscript(stack.back().begin(), stack.back().end()); - vector > vSolutions2; - txnouttype whichType2; - if (!Solver(subscript, whichType2, vSolutions2)) - return false; - if (whichType2 == TX_SCRIPTHASH) - return false; - - int tmpExpected; - tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; - } - - return true; -} - -unsigned int CTransaction::GetLegacySigOpCount() const -{ - unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, vin) - { - nSigOps += txin.scriptSig.GetSigOpCount(false); - } - BOOST_FOREACH(const CTxOut& txout, vout) - { - nSigOps += txout.scriptPubKey.GetSigOpCount(false); - } - return nSigOps; -} - - -int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -{ - CBlock blockTmp; - - if (pblock == NULL) { - CCoins coins; - if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); - if (pindex) { - if (!blockTmp.ReadFromDisk(pindex)) - return 0; - pblock = &blockTmp; - } - } - } - - if (pblock) { - // Update the tx's hashBlock - hashBlock = pblock->GetHash(); - - // Locate the transaction - for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) - if (pblock->vtx[nIndex] == *(CTransaction*)this) - break; - if (nIndex == (int)pblock->vtx.size()) - { - vMerkleBranch.clear(); - nIndex = -1; - printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); - return 0; - } - - // Fill in merkle branch - vMerkleBranch = pblock->GetMerkleBranch(nIndex); - } - - // Is the tx in a block that's in the main chain - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - - return pindexBest->nHeight - pindex->nHeight + 1; -} - - - - - - - -bool CTransaction::CheckTransaction(CValidationState &state) const -{ - // Basic checks that don't depend on any context - if (vin.empty()) - return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); - if (vout.empty()) - return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); - // Size limits - if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); - - // Check for negative or overflow output values - int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - if (txout.nValue < MIN_TXOUT_AMOUNT) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); - if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); - nValueOut += txout.nValue; - if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); - } - - // Check for duplicate inputs - set vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, vin) - { - if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); - vInOutPoints.insert(txin.prevout); - } - - if (IsCoinBase()) - { - if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); - } - else - { - BOOST_FOREACH(const CTxIn& txin, vin) - if (txin.prevout.IsNull()) - return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); - } - - return true; -} - -int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, - enum GetMinFee_mode mode) const -{ - // Base fee is either nMinTxFee or nMinRelayTxFee - int64 nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee; - - unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); - unsigned int nNewBlockSize = nBlockSize + nBytes; - int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; - - if (fAllowFree) - { - if (nBlockSize == 1) - { - // Transactions under 10K are free - // (about 4500 BTC if made of 50 BTC inputs) - if (nBytes < 10000) - nMinFee = 0; - } - else - { - // Free transaction area - if (nNewBlockSize < 27000) - nMinFee = 0; - } - } - - // To limit dust spam, require base fee if any output is less than 0.01 - if (nMinFee < nBaseFee) - { - BOOST_FOREACH(const CTxOut& txout, vout) - if (txout.nValue < CENT) - nMinFee = nBaseFee; - } - - // Raise the price as the block approaches full - if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) - { - if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) - return MAX_MONEY; - nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; -} - -void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) -{ - LOCK(cs); - - std::map::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); - - // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx - while (it != mapNextTx.end() && it->first.hash == hashTx) { - coins.Spend(it->first.n); // and remove those outputs from coins - it++; - } -} - -bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, - bool* pfMissingInputs) -{ - if (pfMissingInputs) - *pfMissingInputs = false; - - if (!tx.CheckTransaction(state)) - return error("CTxMemPool::accept() : CheckTransaction failed"); - - // Coinbase is only valid in a block, not as a loose transaction - if (tx.IsCoinBase()) - return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); - - // To help v0.1.5 clients who would see it as a negative number - if ((int64)tx.nLockTime > std::numeric_limits::max()) - return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); - - // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !tx.IsStandard()) - return error("CTxMemPool::accept() : nonstandard transaction type"); - - // is it already in the memory pool? - uint256 hash = tx.GetHash(); - { - LOCK(cs); - if (mapTx.count(hash)) - return false; - } - - // Check for conflicts with in-memory transactions - CTransaction* ptxOld = NULL; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (mapNextTx.count(outpoint)) - { - // Disable replacement feature for now - return false; - - // Allow replacing with a newer version of the same transaction - if (i != 0) - return false; - ptxOld = mapNextTx[outpoint].ptx; - if (ptxOld->IsFinal()) - return false; - if (!tx.IsNewerThan(*ptxOld)) - return false; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) - return false; - } - break; - } - } - - if (fCheckInputs) - { - CCoinsView dummy; - CCoinsViewCache view(dummy); - - { - LOCK(cs); - CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); - view.SetBackend(viewMemPool); - - // do we already have it? - if (view.HaveCoins(hash)) - return false; - - // do all inputs exist? - // Note that this does not check for the presence of actual outputs (see the next check for that), - // only helps filling in pfMissingInputs (to determine missing vs spent). - BOOST_FOREACH(const CTxIn txin, tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; - return false; - } - } - - // are the actual inputs available? - if (!tx.HaveInputs(view)) - return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); - - // Bring the best block into scope - view.GetBestBlock(); - - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); - } - - // Check for non-standard pay-to-script-hash in inputs - if (!tx.AreInputsStandard(view) && !fTestNet) - return error("CTxMemPool::accept() : nonstandard transaction input"); - - // Note: if you modify this code to accept non-standard transactions, then - // you should add code here to check that the transaction does a - // reasonable number of ECDSA signature verifications. - - int64 nFees = tx.GetValueIn(view)-tx.GetValueOut(); - unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - - // Don't accept it if it can't get into a block - int64 txMinFee = tx.GetMinFee(1000, false, GMF_RELAY); - if (fLimitFree && nFees < txMinFee) - return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, - hash.ToString().c_str(), - nFees, txMinFee); - - // Continuously rate-limit free transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) - { - static double dFreeCount; - static int64 nLastTime; - int64 nNow = GetTime(); - - LOCK(cs); - - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); - if (fDebug) - printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; - } - - // Check against previous transactions - // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) - { - return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); - } - } - - // Store transaction in memory - { - LOCK(cs); - if (ptxOld) - { - printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - remove(*ptxOld); - } - addUnchecked(hash, tx); - } - - ///// are we sure this is ok when loading transactions or restoring block txes - // If updated, erase old tx from wallet - if (ptxOld) - EraseFromWallets(ptxOld->GetHash()); - SyncWithWallets(hash, tx, NULL, true); - - printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - mapTx.size()); - return true; -} - -bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) -{ - try { - return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); - } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); - } -} - -bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) -{ - // Add to memory pool without checking anything. Don't call this directly, - // call CTxMemPool::accept to properly check the transaction first. - { - mapTx[hash] = tx; - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); - nTransactionsUpdated++; - } - return true; -} - - -bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) -{ - // Remove transaction from memory pool - { - LOCK(cs); - uint256 hash = tx.GetHash(); - if (mapTx.count(hash)) - { - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapNextTx.erase(txin.prevout); - mapTx.erase(hash); - nTransactionsUpdated++; - } - } - return true; -} - -bool CTxMemPool::removeConflicts(const CTransaction &tx) -{ - // Remove transactions which depend on inputs of tx, recursively - LOCK(cs); - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - std::map::iterator it = mapNextTx.find(txin.prevout); - if (it != mapNextTx.end()) { - const CTransaction &txConflict = *it->second.ptx; - if (txConflict != tx) - remove(txConflict, true); - } - } - return true; -} - -void CTxMemPool::clear() -{ - LOCK(cs); - mapTx.clear(); - mapNextTx.clear(); - ++nTransactionsUpdated; -} - -void CTxMemPool::queryHashes(std::vector& vtxid) -{ - vtxid.clear(); - - LOCK(cs); - vtxid.reserve(mapTx.size()); - for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back((*mi).first); -} - - - - -int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const -{ - if (hashBlock == 0 || nIndex == -1) - return 0; - - // Find the block it claims to be in - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) - { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; - } - - pindexRet = pindex; - return pindexBest->nHeight - pindex->nHeight + 1; -} - - -int CMerkleTx::GetBlocksToMaturity() const -{ - if (!IsCoinBase()) - return 0; - return max(0, (COINBASE_MATURITY+200) - GetDepthInMainChain()); -} - - -bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) -{ - CValidationState state; - return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); -} - - - -bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) -{ - { - LOCK(mempool.cs); - // Add previous supporting transactions first - BOOST_FOREACH(CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) - tx.AcceptToMemoryPool(fCheckInputs, false); - } - } - return AcceptToMemoryPool(fCheckInputs, false); - } - return false; -} - - -// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock -bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) -{ - CBlockIndex *pindexSlow = NULL; - { - LOCK(cs_main); - { - LOCK(mempool.cs); - if (mempool.exists(hash)) - { - txOut = mempool.lookup(hash); - return true; - } - } - - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - CBlockHeader header; - try { - file >> header; - fseek(file, postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) - return error("%s() : txid mismatch", __PRETTY_FUNCTION__); - return true; - } - } - - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - int nHeight = -1; - { - CCoinsViewCache &view = *pcoinsTip; - CCoins coins; - if (view.GetCoins(hash, coins)) - nHeight = coins.nHeight; - } - if (nHeight > 0) - pindexSlow = FindBlockByHeight(nHeight); - } - } - - if (pindexSlow) { - CBlock block; - if (block.ReadFromDisk(pindexSlow)) { - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - if (tx.GetHash() == hash) { - txOut = tx; - hashBlock = pindexSlow->GetBlockHash(); - return true; - } - } - } - } - - return false; -} - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CBlock and CBlockIndex -// - -static CBlockIndex* pblockindexFBBHLast; -CBlockIndex* FindBlockByHeight(int nHeight) -{ - CBlockIndex *pblockindex; - if (nHeight < nBestHeight / 2) - pblockindex = pindexGenesisBlock; - else - pblockindex = pindexBest; - if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight)) - pblockindex = pblockindexFBBHLast; - while (pblockindex->nHeight > nHeight) - pblockindex = pblockindex->pprev; - while (pblockindex->nHeight < nHeight) - pblockindex = pblockindex->pnext; - pblockindexFBBHLast = pblockindex; - return pblockindex; -} - -bool CBlock::ReadFromDisk(const CBlockIndex* pindex) -{ - if (!ReadFromDisk(pindex->GetBlockPos())) - return false; - if (GetHash() != pindex->GetBlockHash()) - return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); - return true; -} - -uint256 static GetOrphanRoot(const CBlockHeader* pblock) -{ - // Work back to the first block in the orphan chain - while (mapOrphanBlocks.count(pblock->hashPrevBlock)) - pblock = mapOrphanBlocks[pblock->hashPrevBlock]; - return pblock->GetHash(); -} - -int64 static GetBlockValue(int nBits, int64 nFees) -{ - uint64 nSubsidy = 0; - if (!TargetGetMint(nBits, nSubsidy)) - error("GetBlockValue() : invalid mint value"); - return ((int64)nSubsidy) + nFees; -} - -static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week -static const int64 nTargetSpacing = 60; // one minute block spacing - -// -// minimum amount of work that could possibly be required nTime after -// minimum work required was nBase -// -unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) -{ - // primecoin: min work for orphan block takes min work for now - TargetSetLength(nTargetMinLength, nBase); - return nBase; -} - -unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) -{ - unsigned int nBits = TargetGetLimit(); - - // Genesis block - if (pindexLast == NULL) - return nBits; - - const CBlockIndex* pindexPrev = pindexLast; - if (pindexPrev->pprev == NULL) - return TargetGetInitial(); // first block - const CBlockIndex* pindexPrevPrev = pindexPrev->pprev; - if (pindexPrevPrev->pprev == NULL) - return TargetGetInitial(); // second block - - // Primecoin: continuous target adjustment on every block - int64 nInterval = nTargetTimespan / nTargetSpacing; - int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); - if (!TargetGetNext(pindexPrev->nBits, nInterval, nTargetSpacing, nActualSpacing, nBits)) - return error("GetNextWorkRequired() : failed to get next target"); - - if (fDebug && GetBoolArg("-printtarget")) - printf("GetNextWorkRequired() : lastindex=%u prev=0x%08x new=0x%08x\n", - pindexLast->nHeight, pindexPrev->nBits, nBits); - return nBits; -} - -bool CheckProofOfWork(uint256 hashBlockHeader, unsigned int nBits, const CBigNum& bnProbablePrime, unsigned int& nChainType, unsigned int& nChainLength) -{ - if (!CheckPrimeProofOfWork(hashBlockHeader, nBits, bnProbablePrime, nChainType, nChainLength)) - return error("CheckProofOfWork() : check failed for prime proof-of-work"); - return true; -} - -// Return maximum amount of blocks that other nodes claim to have -int GetNumBlocksOfPeers() -{ - return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); -} - -bool IsInitialBlockDownload() -{ - if (pindexBest == NULL || fImporting || fReindex || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) - return true; - static int64 nLastUpdate; - static CBlockIndex* pindexLastBest; - if (pindexBest != pindexLastBest) - { - pindexLastBest = pindexBest; - nLastUpdate = GetTime(); - } - return (GetTime() - nLastUpdate < 10 && - pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); -} - -void static InvalidChainFound(CBlockIndex* pindexNew) -{ - if (pindexNew->nChainWork > nBestInvalidWork) - { - nBestInvalidWork = pindexNew->nChainWork; - pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork)); - uiInterface.NotifyBlocksChanged(); - } - printf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", - pindexNew->GetBlockHash().ToString().c_str(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime()).c_str()); - printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); - if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) - printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); -} - -void static InvalidBlockFound(CBlockIndex *pindex) { - pindex->nStatus |= BLOCK_FAILED_VALID; - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); - setBlockIndexValid.erase(pindex); - InvalidChainFound(pindex); - if (pindex->pnext) { - CValidationState stateDummy; - ConnectBestBlock(stateDummy); // reorganise away from the failed block - } -} - -bool ConnectBestBlock(CValidationState &state) { - do { - CBlockIndex *pindexNewBest; - - { - std::set::reverse_iterator it = setBlockIndexValid.rbegin(); - if (it == setBlockIndexValid.rend()) - return true; - pindexNewBest = *it; - } - - if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->nChainWork == pindexBest->nChainWork)) - return true; // nothing to do - - // check ancestry - CBlockIndex *pindexTest = pindexNewBest; - std::vector vAttach; - do { - if (pindexTest->nStatus & BLOCK_FAILED_MASK) { - // mark descendants failed - CBlockIndex *pindexFailed = pindexNewBest; - while (pindexTest != pindexFailed) { - pindexFailed->nStatus |= BLOCK_FAILED_CHILD; - setBlockIndexValid.erase(pindexFailed); - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed)); - pindexFailed = pindexFailed->pprev; - } - InvalidChainFound(pindexNewBest); - break; - } - - if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) - vAttach.push_back(pindexTest); - - if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { - reverse(vAttach.begin(), vAttach.end()); - BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { - boost::this_thread::interruption_point(); - try { - if (!SetBestChain(state, pindexSwitch)) - return false; - } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); - } - } - return true; - } - pindexTest = pindexTest->pprev; - } while(true); - } while(true); -} - -void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) -{ - nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); -} - - - - - - - - - - - -const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view) -{ - const CCoins &coins = view.GetCoins(input.prevout.hash); - assert(coins.IsAvailable(input.prevout.n)); - return coins.vout[input.prevout.n]; -} - -int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const -{ - if (IsCoinBase()) - return 0; - - int64 nResult = 0; - for (unsigned int i = 0; i < vin.size(); i++) - nResult += GetOutputFor(vin[i], inputs).nValue; - - return nResult; -} - -unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const -{ - if (IsCoinBase()) - return 0; - - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - const CTxOut &prevout = GetOutputFor(vin[i], inputs); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); - } - return nSigOps; -} - -void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const -{ - // mark inputs spent - if (!IsCoinBase()) { - BOOST_FOREACH(const CTxIn &txin, vin) { - CCoins &coins = inputs.GetCoins(txin.prevout.hash); - CTxInUndo undo; - assert(coins.Spend(txin.prevout, undo)); - txundo.vprevout.push_back(undo); - } - } - - // add outputs - assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); -} - -bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const -{ - if (!IsCoinBase()) { - // first check whether information about the prevout hash is available - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - if (!inputs.HaveCoins(prevout.hash)) - return false; - } - - // then check whether the actual outputs are available - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - const CCoins &coins = inputs.GetCoins(prevout.hash); - if (!coins.IsAvailable(prevout.n)) - return false; - } - } - return true; -} - -bool CScriptCheck::operator()() const { - const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) - return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().c_str()); - return true; -} - -bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) -{ - return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); -} - -bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) const -{ - if (!IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(vin.size()); - - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. - if (!HaveInputs(inputs)) - return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().c_str())); - - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; - int64 nValueIn = 0; - int64 nFees = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - const COutPoint &prevout = vin[i].prevout; - const CCoins &coins = inputs.GetCoins(prevout.hash); - - // If prev is coinbase, check that it's matured - if (coins.IsCoinBase()) { - if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) - return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); - } - - // Check for negative or overflow input values - nValueIn += coins.vout[prevout.n].nValue; - if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs() : txin values out of range")); - - } - - if (nValueIn < GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().c_str())); - - // Tally transaction fees - int64 nTxFee = nValueIn - GetValueOut(); - if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().c_str())); - // ppcoin: enforce transaction fees for every block - if (nTxFee < GetMinFee()) - return state.DoS(100, error("CheckInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs() : nFees out of range")); - - // The first loop above does all the inexpensive checks. - // Only if ALL inputs pass do we perform expensive ECDSA signature checks. - // Helps prevent CPU exhaustion attacks. - - // Skip ECDSA signature verification when connecting blocks - // before the last block chain checkpoint. This is safe because block merkle hashes are - // still computed and checked, and any change will be caught at the next checkpoint. - if (fScriptChecks) { - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - const CCoins &coins = inputs.GetCoins(prevout.hash); - - // Verify signature - CScriptCheck check(coins, *this, i, flags, 0); - if (pvChecks) { - pvChecks->push_back(CScriptCheck()); - check.swap(pvChecks->back()); - } else if (!check()) { - if (flags & SCRIPT_VERIFY_STRICTENC) { - // For now, check whether the failure was caused by non-canonical - // encodings or not; if so, don't trigger DoS protection. - CScriptCheck check(coins, *this, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); - if (check()) - return state.Invalid(); - } - return state.DoS(100,false); - } - } - } - } - - return true; -} - - - - -bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) -{ - assert(pindex == view.GetBestBlock()); - - if (pfClean) - *pfClean = false; - - bool fClean = true; - - CBlockUndo blockUndo; - CDiskBlockPos pos = pindex->GetUndoPos(); - if (pos.IsNull()) - return error("DisconnectBlock() : no undo data available"); - if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) - return error("DisconnectBlock() : failure reading undo data"); - - if (blockUndo.vtxundo.size() + 1 != vtx.size()) - return error("DisconnectBlock() : block and undo data inconsistent"); - - // undo transactions in reverse order - for (int i = vtx.size() - 1; i >= 0; i--) { - const CTransaction &tx = vtx[i]; - uint256 hash = tx.GetHash(); - - // check that all outputs are available - if (!view.HaveCoins(hash)) { - fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); - view.SetCoins(hash, CCoins()); - } - CCoins &outs = view.GetCoins(hash); - - CCoins outsBlock = CCoins(tx, pindex->nHeight); - if (outs != outsBlock) - fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); - - // remove outputs - outs = CCoins(); - - // restore inputs - if (i > 0) { // not coinbases - const CTxUndo &txundo = blockUndo.vtxundo[i-1]; - if (txundo.vprevout.size() != tx.vin.size()) - return error("DisconnectBlock() : transaction and undo data inconsistent"); - for (unsigned int j = tx.vin.size(); j-- > 0;) { - const COutPoint &out = tx.vin[j].prevout; - const CTxInUndo &undo = txundo.vprevout[j]; - CCoins coins; - view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent - if (undo.nHeight != 0) { - // undo data contains height: this is the last output of the prevout tx being spent - if (!coins.IsPruned()) - fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); - coins = CCoins(); - coins.fCoinBase = undo.fCoinBase; - coins.nHeight = undo.nHeight; - coins.nVersion = undo.nVersion; - } else { - if (coins.IsPruned()) - fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); - } - if (coins.IsAvailable(out.n)) - fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); - if (coins.vout.size() < out.n+1) - coins.vout.resize(out.n+1); - coins.vout[out.n] = undo.txout; - if (!view.SetCoins(out.hash, coins)) - return error("DisconnectBlock() : cannot restore coin inputs"); - } - } - } - - // move best block pointer to prevout block - view.SetBestBlock(pindex->pprev); - - if (pfClean) { - *pfClean = fClean; - return true; - } else { - return fClean; - } -} - -void static FlushBlockFile(bool fFinalize = false) -{ - LOCK(cs_LastBlockFile); - - CDiskBlockPos posOld(nLastBlockFile, 0); - - FILE *fileOld = OpenBlockFile(posOld); - if (fileOld) { - if (fFinalize) - TruncateFile(fileOld, infoLastBlockFile.nSize); - FileCommit(fileOld); - fclose(fileOld); - } - - fileOld = OpenUndoFile(posOld); - if (fileOld) { - if (fFinalize) - TruncateFile(fileOld, infoLastBlockFile.nUndoSize); - FileCommit(fileOld); - fclose(fileOld); - } -} - -bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); - -static CCheckQueue scriptcheckqueue(128); - -void ThreadScriptCheck() { - RenameThread("primecoin-scriptch"); - scriptcheckqueue.Thread(); -} - -bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) -{ - // Check it again in case a previous version let a bad block in - if (!CheckBlock(state, !fJustCheck, !fJustCheck)) - return false; - - // verify that the view's current state corresponds to the previous block - assert(pindex->pprev == view.GetBestBlock()); - - // Special case for the genesis block, skipping connection of its transactions - // (its coinbase is unspendable) - if (GetHash() == hashGenesisBlock) { - view.SetBestBlock(pindex); - pindexGenesisBlock = pindex; - return true; - } - - bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); - - // Do not allow blocks that contain transactions which 'overwrite' older transactions, - // unless those are already completely spent. - // If such overwrites are allowed, coinbases and transactions depending upon those - // can be duplicated to remove the ability to spend the first instance -- even after - // being sent to another address. - // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. - // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool - // already refuses previously-known transaction ids entirely. - // This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC. - // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the - // two in the chain that violate it. This prevents exploiting the issue against nodes in their - // initial block download. - bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. - !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || - (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); - if (fEnforceBIP30) { - for (unsigned int i=0; inTime >= nBIP16SwitchTime); - - unsigned int flags = SCRIPT_VERIFY_NOCACHE | - (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); - - CBlockUndo blockundo; - - CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - - int64 nStart = GetTimeMicros(); - int64 nFees = 0; - int64 nValueIn = 0; - int64 nValueOut = 0; - int nInputs = 0; - unsigned int nSigOps = 0; - CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size())); - std::vector > vPos; - vPos.reserve(vtx.size()); - for (unsigned int i=0; i MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops")); - - if (tx.IsCoinBase()) - nValueOut += tx.GetValueOut(); - else - { - if (!tx.HaveInputs(view)) - return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); - - if (fStrictPayToScriptHash) - { - // Add in sigops done by pay-to-script-hash inputs; - // this is to prevent a "rogue miner" from creating - // an incredibly-expensive-to-validate block. - nSigOps += tx.GetP2SHSigOpCount(view); - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops")); - } - - int64 nTxValueIn = tx.GetValueIn(view); - int64 nTxValueOut = tx.GetValueOut(); - nValueIn += nTxValueIn; - nValueOut += nTxValueOut; - nFees += nTxValueIn-nTxValueOut; - - std::vector vChecks; - if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) - return false; - control.Add(vChecks); - } - - CTxUndo txundo; - tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i)); - if (!tx.IsCoinBase()) - blockundo.vtxundo.push_back(txundo); - - vPos.push_back(std::make_pair(GetTxHash(i), pos)); - pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); - } - - if (!fJustCheck) - { - // primecoin: track money supply - pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn; - CDiskBlockIndex blockindex(pindex); - if (!pblocktree->WriteBlockIndex(blockindex)) - return state.Abort(_("Failed to write block index for moneysupply")); - } - - int64 nTime = GetTimeMicros() - nStart; - if (fBenchmark) - printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - - if (vtx[0].GetValueOut() > GetBlockValue(pindex->nBits, nFees) - vtx[0].GetMinFee() + MIN_TX_FEE) - return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%s vs limit=%s)", FormatMoney(vtx[0].GetValueOut()).c_str(), FormatMoney(GetBlockValue(pindex->nBits, nFees) - vtx[0].GetMinFee() + MIN_TX_FEE).c_str())); - - if (!control.Wait()) - return state.DoS(100, false); - int64 nTime2 = GetTimeMicros() - nStart; - if (fBenchmark) - printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); - - if (fJustCheck) - return true; - - // Write undo information to disk - if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) - { - if (pindex->GetUndoPos().IsNull()) { - CDiskBlockPos pos; - if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) - return error("ConnectBlock() : FindUndoPos failed"); - if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) - return state.Abort(_("Failed to write undo data")); - - // update nUndoPos in block index - pindex->nUndoPos = pos.nPos; - pindex->nStatus |= BLOCK_HAVE_UNDO; - } - - pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; - - CDiskBlockIndex blockindex(pindex); - if (!pblocktree->WriteBlockIndex(blockindex)) - return state.Abort(_("Failed to write block index")); - } - - if (fTxIndex) - if (!pblocktree->WriteTxIndex(vPos)) - return state.Abort(_("Failed to write transaction index")); - - // add this block to the view's block chain - assert(view.SetBestBlock(pindex)); - - // Watch for transactions paying to me - for (unsigned int i=0; inHeight > pfork->nHeight) { - plonger = plonger->pprev; - assert(plonger != NULL); - } - if (pfork == plonger) - break; - pfork = pfork->pprev; - assert(pfork != NULL); - } - - // List of what to disconnect (typically nothing) - vector vDisconnect; - for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) - vDisconnect.push_back(pindex); - - // List of what to connect (typically only pindexNew) - vector vConnect; - for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) - vConnect.push_back(pindex); - reverse(vConnect.begin(), vConnect.end()); - - if (vDisconnect.size() > 0) { - printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..\n", vDisconnect.size(), pfork->GetBlockHash().ToString().c_str()); - printf("REORGANIZE: Connect %"PRIszu" blocks; ..%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString().c_str()); - } - - // Disconnect shorter branch - vector vResurrect; - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { - CBlock block; - if (!block.ReadFromDisk(pindex)) - return state.Abort(_("Failed to read block")); - int64 nStart = GetTimeMicros(); - if (!block.DisconnectBlock(state, pindex, view)) - return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); - if (fBenchmark) - printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - - // Queue memory transactions to resurrect. - // We only do this for blocks after the last checkpoint (reorganisation before that - // point should only happen with -reindex/-loadblock, or a misbehaving peer. - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) - vResurrect.push_back(tx); - } - - // Connect longer branch - vector vDelete; - BOOST_FOREACH(CBlockIndex *pindex, vConnect) { - CBlock block; - if (!block.ReadFromDisk(pindex)) - return state.Abort(_("Failed to read block")); - int64 nStart = GetTimeMicros(); - if (!block.ConnectBlock(state, pindex, view)) { - if (state.IsInvalid()) { - InvalidChainFound(pindexNew); - InvalidBlockFound(pindex); - } - return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); - } - if (fBenchmark) - printf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - - // Queue memory transactions to delete - BOOST_FOREACH(const CTransaction& tx, block.vtx) - vDelete.push_back(tx); - } - - // Flush changes to global coin state - int64 nStart = GetTimeMicros(); - int nModified = view.GetCacheSize(); - assert(view.Flush()); - int64 nTime = GetTimeMicros() - nStart; - if (fBenchmark) - printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); - - // Make sure it's successfully written to disk before changing memory structure - bool fIsInitialDownload = IsInitialBlockDownload(); - if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { - // Typical CCoins structures on disk are around 100 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error(); - FlushBlockFile(); - pblocktree->Sync(); - if (!pcoinsTip->Flush()) - return state.Abort(_("Failed to write to coin database")); - } - - // At this point, all changes have been done to the database. - // Proceed by updating the memory structures. - - // Disconnect shorter branch - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) - if (pindex->pprev) - pindex->pprev->pnext = NULL; - - // Connect longer branch - BOOST_FOREACH(CBlockIndex* pindex, vConnect) - if (pindex->pprev) - pindex->pprev->pnext = pindex; - - // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) { - // ignore validation errors in resurrected transactions - CValidationState stateDummy; - tx.AcceptToMemoryPool(stateDummy, true, false); - } - - // Delete redundant memory transactions that are in the connected branch - BOOST_FOREACH(CTransaction& tx, vDelete) { - mempool.remove(tx); - mempool.removeConflicts(tx); - } - - // Update best block in wallet (so we can detect restored wallets) - if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) - { - const CBlockLocator locator(pindexNew); - ::SetBestChain(locator); - } - - // New best block - hashBestChain = pindexNew->GetBlockHash(); - pindexBest = pindexNew; - pblockindexFBBHLast = NULL; - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexNew->nChainWork; - nTimeBestReceived = GetTime(); - nTransactionsUpdated++; - uint256 nBlockWork = pindexNew->nChainWork - (pindexNew->pprev? pindexNew->pprev->nChainWork : 0); - printf("SetBestChain: new best=%s height=%d difficulty=%.8g log2Work=%.8g log2ChainWork=%.8g tx=%lu date=%s progress=%f\n", - hashBestChain.ToString().c_str(), nBestHeight, GetPrimeDifficulty(pindexNew->nBits), log(nBlockWork.getdouble())/log(2.0), log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str(), - Checkpoints::GuessVerificationProgress(pindexBest)); - - // Check the version of the last 100 blocks to see if we need to upgrade: - if (!fIsInitialDownload) - { - int nUpgraded = 0; - const CBlockIndex* pindex = pindexBest; - for (int i = 0; i < 100 && pindex != NULL; i++) - { - if (pindex->nVersion > CBlock::CURRENT_VERSION) - ++nUpgraded; - pindex = pindex->pprev; - } - if (nUpgraded > 0) - printf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); - if (nUpgraded > 100/2) - // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); - } - - if (!IsSyncCheckpointEnforced()) // checkpoint advisory mode - { - if (pindexBest->pprev && !CheckSyncCheckpoint(pindexBest->GetBlockHash(), pindexBest->pprev)) - strCheckpointWarning = _("Warning: checkpoint on different blockchain fork, contact developers to resolve the issue"); - else - strCheckpointWarning = ""; - } - - std::string strCmd = GetArg("-blocknotify", ""); - - if (!fIsInitialDownload && !strCmd.empty()) - { - boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free - } - - return true; -} - - -bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) -{ - // Check for duplicate - uint256 hash = GetHash(); - if (mapBlockIndex.count(hash)) - return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str())); - - // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(*this); - assert(pindexNew); - map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); - if (miPrev != mapBlockIndex.end()) - { - pindexNew->pprev = (*miPrev).second; - pindexNew->nHeight = pindexNew->pprev->nHeight + 1; - } - pindexNew->nTx = vtx.size(); - pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256(); - pindexNew->nPrimeChainType = nPrimeChainType; - pindexNew->nPrimeChainLength = nPrimeChainLength; - pindexNew->nWorkTransition = EstimateWorkTransition((pindexNew->pprev ? pindexNew->pprev->nWorkTransition : TargetGetInitial()), nBits, nPrimeChainLength); - pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; - pindexNew->nFile = pos.nFile; - pindexNew->nDataPos = pos.nPos; - pindexNew->nUndoPos = 0; - pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; - setBlockIndexValid.insert(pindexNew); - - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) - return state.Abort(_("Failed to write block index")); - - // New best? - if (!ConnectBestBlock(state)) - return false; - - if (pindexNew == pindexBest) - { - // Notify UI to display prev block's coinbase if it was ours - static uint256 hashPrevBestCoinBase; - UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = GetTxHash(0); - } - - if (!pblocktree->Flush()) - return state.Abort(_("Failed to sync block index")); - - uiInterface.NotifyBlocksChanged(); - return true; -} - - -bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) -{ - bool fUpdatedLast = false; - - LOCK(cs_LastBlockFile); - - if (fKnown) { - if (nLastBlockFile != pos.nFile) { - nLastBlockFile = pos.nFile; - infoLastBlockFile.SetNull(); - pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); - fUpdatedLast = true; - } - } else { - while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); - FlushBlockFile(true); - nLastBlockFile++; - infoLastBlockFile.SetNull(); - pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine - fUpdatedLast = true; - } - pos.nFile = nLastBlockFile; - pos.nPos = infoLastBlockFile.nSize; - } - - infoLastBlockFile.nSize += nAddSize; - infoLastBlockFile.AddBlock(nHeight, nTime); - - if (!fKnown) { - unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - if (nNewChunks > nOldChunks) { - if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { - FILE *file = OpenBlockFile(pos); - if (file) { - printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); - fclose(file); - } - } - else - return state.Error(); - } - } - - if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return state.Abort(_("Failed to write file info")); - if (fUpdatedLast) - pblocktree->WriteLastBlockFile(nLastBlockFile); - - return true; -} - -bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) -{ - pos.nFile = nFile; - - LOCK(cs_LastBlockFile); - - unsigned int nNewSize; - if (nFile == nLastBlockFile) { - pos.nPos = infoLastBlockFile.nUndoSize; - nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); - if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return state.Abort(_("Failed to write block info")); - } else { - CBlockFileInfo info; - if (!pblocktree->ReadBlockFileInfo(nFile, info)) - return state.Abort(_("Failed to read block info")); - pos.nPos = info.nUndoSize; - nNewSize = (info.nUndoSize += nAddSize); - if (!pblocktree->WriteBlockFileInfo(nFile, info)) - return state.Abort(_("Failed to write block info")); - } - - unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; - unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; - if (nNewChunks > nOldChunks) { - if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { - FILE *file = OpenUndoFile(pos); - if (file) { - printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); - fclose(file); - } - } - else - return state.Error(); - } - - return true; -} - - -bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const -{ - // These are checks that are independent of context - // that can be verified before saving an orphan block. - - // Size limits - if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock() : size limits failed")); - - // Special short-term limits to avoid 10,000 BDB lock limit: - if (GetBlockTime() >= 1363867200 && // start enforcing 21 March 2013, noon GMT - GetBlockTime() < 1368576000) // stop enforcing 15 May 2013 00:00:00 - { - // Rule is: #unique txids referenced <= 4,500 - // ... to prevent 10,000 BDB lock exhaustion on old clients - set setTxIn; - for (size_t i = 0; i < vtx.size(); i++) - { - setTxIn.insert(vtx[i].GetHash()); - if (i == 0) continue; // skip coinbase txin - BOOST_FOREACH(const CTxIn& txin, vtx[i].vin) - setTxIn.insert(txin.prevout.hash); - } - size_t nTxids = setTxIn.size(); - if (nTxids > 4500) - return error("CheckBlock() : 15 May maxlocks violation"); - } - - // Primecoin: proof of work is checked in ProcessBlock() - - // Check timestamp - if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); - - // First transaction must be coinbase, the rest must not be - if (vtx.empty() || !vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); - for (unsigned int i = 1; i < vtx.size(); i++) - if (vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : more than one coinbase")); - - // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction(state)) - return error("CheckBlock() : CheckTransaction failed"); - - // Build the merkle tree already. We need it anyway later, and it makes the - // block cache the transaction hashes, which means they don't need to be - // recalculated many times during this block's validation. - BuildMerkleTree(); - - // Check for duplicate txids. This is caught by ConnectInputs(), - // but catching it earlier avoids a potential DoS attack: - set uniqueTx; - for (unsigned int i=0; i MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); - - // Check merkle root - if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) - return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); - - return true; -} - -bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) -{ - // Check for duplicate - uint256 hash = GetHash(); - if (mapBlockIndex.count(hash)) - return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); - - // Get prev block index - CBlockIndex* pindexPrev = NULL; - int nHeight = 0; - if (hash != hashGenesisBlock) { - map::iterator mi = mapBlockIndex.find(hashPrevBlock); - if (mi == mapBlockIndex.end()) - return state.DoS(10, error("AcceptBlock() : prev block not found")); - pindexPrev = (*mi).second; - nHeight = pindexPrev->nHeight+1; - - // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev, this)) - return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); - - // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); - - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.IsFinal(nHeight, GetBlockTime())) - return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); - - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); - - // ppcoin: check that the block satisfies synchronized checkpoint - if (IsSyncCheckpointEnforced() // checkpoint enforce mode - && !CheckSyncCheckpoint(hash, pindexPrev)) - return error("AcceptBlock() : rejected by synchronized checkpoint"); - - // Primecoin: block version starts from 2 - if (nVersion < 2) - return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (nVersion >= 2) - { - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) - { - CScript expect = CScript() << nHeight; - if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); - } - } - } - - // Write block to history file - try { - unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != NULL) - blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) - return error("AcceptBlock() : FindBlockPos failed"); - if (dbp == NULL) - if (!WriteToDisk(blockPos)) - return state.Abort(_("Failed to write block")); - if (!AddToBlockIndex(state, blockPos)) - return error("AcceptBlock() : AddToBlockIndex failed"); - } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); - } - - // Relay inventory, but don't relay old inventory during initial block download - int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - if (hashBestChain == hash) - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hash)); - } - - // ppcoin: check pending sync-checkpoint - AcceptPendingSyncCheckpoint(); - - return true; -} - -// Get block work value for main chain protocol -CBigNum CBlockIndex::GetBlockWork() const -{ - // Primecoin: - // Difficulty multiplier of extra prime is estimated by nWorkTransitionRatio - // Difficulty multiplier of fractional is estimated by - // r = 1/TransitionRatio - // length >= n discovery rate = 1 - // length > n discovery rate = 1/TransitionRatio - // length == n discovery rate: 1 - 1/TransitionRatio - // meeting target rate 1/FractionalDiff * (1 - 1/TransitionRatio) + 1/TranstionRatio - // fractionalDiff = nFractionalDiffculty / nFractionalDifficultyMin - // fractional multiplier = 1 / meeting target rate - // = (TransitionRatio * FractionalDiff) / (TransitionRatio - 1 + FractionalDiff) - uint64 nFractionalDifficulty = TargetGetFractionalDifficulty(nBits); - CBigNum bnWork = 256; - for (unsigned int nCount = nTargetMinLength; nCount < TargetGetLength(nBits); nCount++) - bnWork *= nWorkTransitionRatio; - bnWork *= ((uint64) nWorkTransitionRatio) * nFractionalDifficulty; - bnWork /= (((uint64) nWorkTransitionRatio - 1) * nFractionalDifficultyMin + nFractionalDifficulty); - return bnWork; -} - -bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) -{ - unsigned int nFound = 0; - for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) - { - if (pstart->nVersion >= minVersion) - ++nFound; - pstart = pstart->pprev; - } - return (nFound >= nRequired); -} - -bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) -{ - // Check for duplicate - uint256 hash = pblock->GetHash(); - if (mapBlockIndex.count(hash)) - return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().c_str())); - if (mapOrphanBlocks.count(hash)) - return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str())); - - // Preliminary checks - if (!pblock->CheckBlock(state)) - return error("ProcessBlock() : CheckBlock FAILED"); - - // Check proof of work matches claimed amount - if (!CheckProofOfWork(pblock->GetHeaderHash(), pblock->nBits, pblock->bnPrimeChainMultiplier, pblock->nPrimeChainType, pblock->nPrimeChainLength)) - return state.DoS(100, error("ProcessBlock() : proof of work failed")); - - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) - { - // Extra checks to prevent "fill up memory by spamming with bogus blocks" - int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; - if (deltaTime < 0) - { - return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); - } - unsigned int nRequired = ComputeMinWork(pcheckpoint->nBits, deltaTime); - if (pblock->nBits < nRequired) - { - return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work nBits=%s required=%s", TargetToString(pblock->nBits).c_str(), TargetToString(nRequired).c_str())); - } - } - - // ppcoin: ask for pending sync-checkpoint if any - if (!IsInitialBlockDownload()) - AskForPendingSyncCheckpoint(pfrom); - - // If we don't already have its previous block, shunt it off to holding area until we get it - if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) - { - printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().c_str()); - - // Accept orphans as long as there is a node to request its parents from - if (pfrom) { - CBlock* pblock2 = new CBlock(*pblock); - mapOrphanBlocks.insert(make_pair(hash, pblock2)); - mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); - - // Ask this guy to fill in what we're missing - pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); - } - return true; - } - - // Store to disk - if (!pblock->AcceptBlock(state, dbp)) - return error("ProcessBlock() : AcceptBlock FAILED"); - - // Recursively process any orphan blocks that depended on this one - vector vWorkQueue; - vWorkQueue.push_back(hash); - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hashPrev = vWorkQueue[i]; - for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); - mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); - ++mi) - { - CBlock* pblockOrphan = (*mi).second; - // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) - CValidationState stateDummy; - if (pblockOrphan->AcceptBlock(stateDummy)) - vWorkQueue.push_back(pblockOrphan->GetHash()); - mapOrphanBlocks.erase(pblockOrphan->GetHash()); - delete pblockOrphan; - } - mapOrphanBlocksByPrev.erase(hashPrev); - } - - printf("ProcessBlock: ACCEPTED\n"); - - // ppcoin: if responsible for sync-checkpoint send it - if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty() && - (int)GetArg("-checkpointdepth", -1) >= 0) - SendSyncCheckpoint(AutoSelectSyncCheckpoint()); - - return true; -} - - - - - - - - -CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) -{ - header = block.GetBlockHeader(); - - vector vMatch; - vector vHashes; - - vMatch.reserve(block.vtx.size()); - vHashes.reserve(block.vtx.size()); - - for (unsigned int i = 0; i < block.vtx.size(); i++) - { - uint256 hash = block.vtx[i].GetHash(); - if (filter.IsRelevantAndUpdate(block.vtx[i], hash)) - { - vMatch.push_back(true); - vMatchedTxn.push_back(make_pair(i, hash)); - } - else - vMatch.push_back(false); - vHashes.push_back(hash); - } - - txn = CPartialMerkleTree(vHashes, vMatch); -} - - - - - - - - -uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector &vTxid) { - if (height == 0) { - // hash at height 0 is the txids themself - return vTxid[pos]; - } else { - // calculate left hash - uint256 left = CalcHash(height-1, pos*2, vTxid), right; - // calculate right hash if not beyong the end of the array - copy left hash otherwise1 - if (pos*2+1 < CalcTreeWidth(height-1)) - right = CalcHash(height-1, pos*2+1, vTxid); - else - right = left; - // combine subhashes - return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); - } -} - -void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch) { - // determine whether this node is the parent of at least one matched txid - bool fParentOfMatch = false; - for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++) - fParentOfMatch |= vMatch[p]; - // store as flag bit - vBits.push_back(fParentOfMatch); - if (height==0 || !fParentOfMatch) { - // if at height 0, or nothing interesting below, store hash and stop - vHash.push_back(CalcHash(height, pos, vTxid)); - } else { - // otherwise, don't store any hash, but descend into the subtrees - TraverseAndBuild(height-1, pos*2, vTxid, vMatch); - if (pos*2+1 < CalcTreeWidth(height-1)) - TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch); - } -} - -uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch) { - if (nBitsUsed >= vBits.size()) { - // overflowed the bits array - failure - fBad = true; - return 0; - } - bool fParentOfMatch = vBits[nBitsUsed++]; - if (height==0 || !fParentOfMatch) { - // if at height 0, or nothing interesting below, use stored hash and do not descend - if (nHashUsed >= vHash.size()) { - // overflowed the hash array - failure - fBad = true; - return 0; - } - const uint256 &hash = vHash[nHashUsed++]; - if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid - vMatch.push_back(hash); - return hash; - } else { - // otherwise, descend into the subtrees to extract matched txids and hashes - uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right; - if (pos*2+1 < CalcTreeWidth(height-1)) - right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch); - else - right = left; - // and combine them before returning - return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); - } -} - -CPartialMerkleTree::CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch) : nTransactions(vTxid.size()), fBad(false) { - // reset state - vBits.clear(); - vHash.clear(); - - // calculate height of tree - int nHeight = 0; - while (CalcTreeWidth(nHeight) > 1) - nHeight++; - - // traverse the partial tree - TraverseAndBuild(nHeight, 0, vTxid, vMatch); -} - -CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {} - -uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch) { - vMatch.clear(); - // An empty set will not work - if (nTransactions == 0) - return 0; - // check for excessively high numbers of transactions - if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction - return 0; - // there can never be more hashes provided than one for every txid - if (vHash.size() > nTransactions) - return 0; - // there must be at least one bit per node in the partial tree, and at least one node per hash - if (vBits.size() < vHash.size()) - return 0; - // calculate height of tree - int nHeight = 0; - while (CalcTreeWidth(nHeight) > 1) - nHeight++; - // traverse the partial tree - unsigned int nBitsUsed = 0, nHashUsed = 0; - uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch); - // verify that no problems occured during the tree traversal - if (fBad) - return 0; - // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) - if ((nBitsUsed+7)/8 != (vBits.size()+7)/8) - return 0; - // verify that all hashes were consumed - if (nHashUsed != vHash.size()) - return 0; - return hashMerkleRoot; -} - - - - - - - -bool AbortNode(const std::string &strMessage) { - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; -} - -bool CheckDiskSpace(uint64 nAdditionalBytes) -{ - uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; - - // Check for nMinDiskSpace bytes (currently 50MB) - if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) - return AbortNode(_("Error: Disk space is low!")); - - return true; -} - -CCriticalSection cs_LastBlockFile; -CBlockFileInfo infoLastBlockFile; -int nLastBlockFile = 0; - -FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) -{ - if (pos.IsNull()) - return NULL; - boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); - boost::filesystem::create_directories(path.parent_path()); - FILE* file = fopen(path.string().c_str(), "rb+"); - if (!file && !fReadOnly) - file = fopen(path.string().c_str(), "wb+"); - if (!file) { - printf("Unable to open file %s\n", path.string().c_str()); - return NULL; - } - if (pos.nPos) { - if (fseek(file, pos.nPos, SEEK_SET)) { - printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); - fclose(file); - return NULL; - } - } - return file; -} - -FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) { - return OpenDiskFile(pos, "blk", fReadOnly); -} - -FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { - return OpenDiskFile(pos, "rev", fReadOnly); -} - -CBlockIndex * InsertBlockIndex(uint256 hash) -{ - if (hash == 0) - return NULL; - - // Return existing - map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - return (*mi).second; - - // Create new - CBlockIndex* pindexNew = new CBlockIndex(); - if (!pindexNew) - throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); - mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - - return pindexNew; -} - -bool static LoadBlockIndexDB() -{ - GeneratePrimeTable(); - - if (!pblocktree->LoadBlockIndexGuts()) - return false; - - boost::this_thread::interruption_point(); - - // Calculate nChainWork - vector > vSortedByHeight; - vSortedByHeight.reserve(mapBlockIndex.size()); - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) - { - CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); - } - sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) - { - CBlockIndex* pindex = item.second; - pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork().getuint256(); - pindex->nWorkTransition = EstimateWorkTransition((pindex->pprev ? pindex->pprev->nWorkTransition : TargetGetInitial()), pindex->nBits, pindex->nPrimeChainLength); - pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) - setBlockIndexValid.insert(pindex); - } - - // Load block file info - pblocktree->ReadLastBlockFile(nLastBlockFile); - printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile); - if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - printf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); - - // ppcoin: load hashSyncCheckpoint - if (!pblocktree->ReadSyncCheckpoint(hashSyncCheckpoint)) - printf("LoadBlockIndexDB(): synchronized checkpoint not read\n"); - else - printf("LoadBlockIndexDB(): synchronized checkpoint %s\n", hashSyncCheckpoint.ToString().c_str()); - - // Load nBestInvalidWork, OK if it doesn't exist - CBigNum bnBestInvalidWork; - pblocktree->ReadBestInvalidWork(bnBestInvalidWork); - nBestInvalidWork = bnBestInvalidWork.getuint256(); - - // Check whether we need to continue reindexing - bool fReindexing = false; - pblocktree->ReadReindexing(fReindexing); - fReindex |= fReindexing; - - // Check whether we have a transaction index - pblocktree->ReadFlag("txindex", fTxIndex); - printf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); - - // Load hashBestChain pointer to end of best chain - pindexBest = pcoinsTip->GetBestBlock(); - if (pindexBest == NULL) - return true; - hashBestChain = pindexBest->GetBlockHash(); - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexBest->nChainWork; - - // set 'next' pointers in best chain - CBlockIndex *pindex = pindexBest; - while(pindex != NULL && pindex->pprev != NULL) { - CBlockIndex *pindexPrev = pindex->pprev; - pindexPrev->pnext = pindex; - pindex = pindexPrev; - } - printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); - - return true; -} - -bool VerifyDB() { - if (pindexBest == NULL || pindexBest->pprev == NULL) - return true; - - // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 3); - int nCheckDepth = GetArg( "-checkblocks", 288); - if (nCheckDepth == 0) - nCheckDepth = 1000000000; // suffices until the year 19000 - if (nCheckDepth > nBestHeight) - nCheckDepth = nBestHeight; - nCheckLevel = std::max(0, std::min(4, nCheckLevel)); - printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CCoinsViewCache coins(*pcoinsTip, true); - CBlockIndex* pindexState = pindexBest; - CBlockIndex* pindexFailure = NULL; - int nGoodTransactions = 0; - CValidationState state; - for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) - { - boost::this_thread::interruption_point(); - if (pindex->nHeight < nBestHeight-nCheckDepth) - break; - CBlock block; - // check level 0: read from disk - if (!block.ReadFromDisk(pindex)) - return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - // check level 1: verify block validity - if (nCheckLevel >= 1 && !block.CheckBlock(state)) - return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - // check level 2: verify undo validity - if (nCheckLevel >= 2 && pindex) { - CBlockUndo undo; - CDiskBlockPos pos = pindex->GetUndoPos(); - if (!pos.IsNull()) { - if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) - return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - } - } - // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { - bool fClean = true; - if (!block.DisconnectBlock(state, pindex, coins, &fClean)) - return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - pindexState = pindex->pprev; - if (!fClean) { - nGoodTransactions = 0; - pindexFailure = pindex; - } else - nGoodTransactions += block.vtx.size(); - } - } - if (pindexFailure) - return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); - - // check level 4: try reconnecting blocks - if (nCheckLevel >= 4) { - CBlockIndex *pindex = pindexState; - while (pindex != pindexBest) { - boost::this_thread::interruption_point(); - pindex = pindex->pnext; - CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - if (!block.ConnectBlock(state, pindex, coins)) - return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - } - } - - printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); - - return true; -} - -void UnloadBlockIndex() -{ - mapBlockIndex.clear(); - setBlockIndexValid.clear(); - pindexGenesisBlock = NULL; - nBestHeight = 0; - nBestChainWork = 0; - nBestInvalidWork = 0; - hashBestChain = 0; - pindexBest = NULL; -} - -bool LoadBlockIndex() -{ - if (fTestNet) - { - pchMessageStart[0] = 0xfb; - pchMessageStart[1] = 0xfe; - pchMessageStart[2] = 0xcb; - pchMessageStart[3] = 0xc3; - hashGenesisBlock = hashGenesisBlockTestNet; - nTargetInitialLength = 5; // primecoin: initial prime chain target - nTargetMinLength = 2; // primecoin: minimum prime chain target - } - - // - // Load block index from databases - // - if (!fReindex && !LoadBlockIndexDB()) - return false; - - return true; -} - - -bool InitBlockIndex() { - // Check whether we're already initialized - if (pindexGenesisBlock != NULL) - return true; - - // Use the provided setting for -txindex in the new database - fTxIndex = GetBoolArg("-txindex", false); - pblocktree->WriteFlag("txindex", fTxIndex); - printf("Initializing databases...\n"); - - // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) - if (!fReindex) { - // Genesis Block: - // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) - // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) - // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) - // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) - // vMerkleTree: 4a5e1e - - // Genesis block - const char* pszDedication = "Sunny King - dedicated to Satoshi Nakamoto and all who have fought for the freedom of mankind"; - CTransaction txNew; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 0 << CBigNum(999) << vector((const unsigned char*)pszDedication, (const unsigned char*)pszDedication + strlen(pszDedication)); - txNew.vout[0].nValue = COIN; - txNew.vout[0].scriptPubKey = CScript(); - CBlock block; - block.vtx.push_back(txNew); - block.hashPrevBlock = 0; - block.hashMerkleRoot = block.BuildMerkleTree(); - block.nTime = 1373064429; - block.nBits = TargetFromInt(6); - block.nNonce = 383; - block.bnPrimeChainMultiplier = ((uint64) 532541) * (uint64)(2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23); - - if (fTestNet) - { - block.nTime = 1373063882; - block.nNonce = 1513; - block.bnPrimeChainMultiplier = ((uint64) 585641) * (uint64)(2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23); - } - - //// debug print - uint256 hash = block.GetHash(); - printf("%s\n", hash.ToString().c_str()); - printf("%s\n", hashGenesisBlock.ToString().c_str()); - printf("%s\n", block.hashMerkleRoot.ToString().c_str()); - assert(block.hashMerkleRoot == uint256("0xaca30eb61dffbb9412d0ae743c3d74554f710853daec40ebd2514e830e05c9ff")); - block.print(); - assert(hash == hashGenesisBlock); - { - CValidationState state; - assert(block.CheckBlock(state, true, true)); - assert(CheckProofOfWork(block.GetHeaderHash(), block.nBits, block.bnPrimeChainMultiplier, block.nPrimeChainType, block.nPrimeChainLength)); - } - - // Start new block file - try { - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - CValidationState state; - if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) - return error("LoadBlockIndex() : FindBlockPos failed"); - if (!block.WriteToDisk(blockPos)) - return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(state, blockPos)) - return error("LoadBlockIndex() : genesis block not accepted"); - - // ppcoin: initialize synchronized checkpoint - if (!WriteSyncCheckpoint(hashGenesisBlock)) - return error("LoadBlockIndex() : failed to init sync checkpoint"); - } catch(std::runtime_error &e) { - return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); - } - } - - // ppcoin: if checkpoint master key changed must reset sync-checkpoint - if (!CheckCheckpointPubKey()) - return error("LoadBlockIndex() : failed to reset checkpoint master pubkey"); - - return true; -} - - - -void PrintBlockTree() -{ - // pre-compute tree structure - map > mapNext; - for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) - { - CBlockIndex* pindex = (*mi).second; - mapNext[pindex->pprev].push_back(pindex); - // test - //while (rand() % 3 == 0) - // mapNext[pindex->pprev].push_back(pindex); - } - - vector > vStack; - vStack.push_back(make_pair(0, pindexGenesisBlock)); - - int nPrevCol = 0; - while (!vStack.empty()) - { - int nCol = vStack.back().first; - CBlockIndex* pindex = vStack.back().second; - vStack.pop_back(); - - // print split or gap - if (nCol > nPrevCol) - { - for (int i = 0; i < nCol-1; i++) - printf("| "); - printf("|\\\n"); - } - else if (nCol < nPrevCol) - { - for (int i = 0; i < nCol; i++) - printf("| "); - printf("|\n"); - } - nPrevCol = nCol; - - // print columns - for (int i = 0; i < nCol; i++) - printf("| "); - - // print item - CBlock block; - block.ReadFromDisk(pindex); - printf("%d (blk%05u.dat:0x%x) %s %s tx %"PRIszu"", - pindex->nHeight, - pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(), - TargetToString(block.nBits).c_str(), block.vtx.size()); - - PrintWallets(block); - - // put the main time-chain first - vector& vNext = mapNext[pindex]; - for (unsigned int i = 0; i < vNext.size(); i++) - { - if (vNext[i]->pnext) - { - swap(vNext[0], vNext[i]); - break; - } - } - - // iterate children - for (unsigned int i = 0; i < vNext.size(); i++) - vStack.push_back(make_pair(nCol+i, vNext[i])); - } -} - -bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) -{ - int64 nStart = GetTimeMillis(); - - int nLoaded = 0; - try { - CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); - uint64 nStartByte = 0; - if (dbp) { - // (try to) skip already indexed part - CBlockFileInfo info; - if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { - nStartByte = info.nSize; - blkdat.Seek(info.nSize); - } - } - uint64 nRewind = blkdat.GetPos(); - while (blkdat.good() && !blkdat.eof()) { - boost::this_thread::interruption_point(); - - blkdat.SetPos(nRewind); - nRewind++; // start one byte further next time, in case of failure - blkdat.SetLimit(); // remove former limit - unsigned int nSize = 0; - try { - // locate a header - unsigned char buf[4]; - blkdat.FindByte(pchMessageStart[0]); - nRewind = blkdat.GetPos()+1; - blkdat >> FLATDATA(buf); - if (memcmp(buf, pchMessageStart, 4)) - continue; - // read size - blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) - continue; - } catch (std::exception &e) { - // no valid block header found; don't complain - break; - } - try { - // read block - uint64 nBlockPos = blkdat.GetPos(); - blkdat.SetLimit(nBlockPos + nSize); - CBlock block; - blkdat >> block; - nRewind = blkdat.GetPos(); - - // process block - if (nBlockPos >= nStartByte) { - LOCK(cs_main); - if (dbp) - dbp->nPos = nBlockPos; - CValidationState state; - if (ProcessBlock(state, NULL, &block, dbp)) - nLoaded++; - if (state.IsError()) - break; - } - } catch (std::exception &e) { - printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); - } - } - fclose(fileIn); - } catch(std::runtime_error &e) { - AbortNode(_("Error: system error: ") + e.what()); - } - if (nLoaded > 0) - printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); - return nLoaded > 0; -} - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CAlert -// - -extern map mapAlerts; -extern CCriticalSection cs_mapAlerts; - -string GetWarnings(string strFor) -{ - int nPriority = 0; - string strStatusBar; - string strRPC; - - if (GetBoolArg("-testsafemode")) - strRPC = "test"; - - if (!CLIENT_VERSION_IS_RELEASE) - strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); - - // Checkpoint warning - if (strCheckpointWarning != "") - { - nPriority = 900; - strStatusBar = strCheckpointWarning; - } - - // Misc warnings like out of disk space and clock is wrong - if (strMiscWarning != "") - { - nPriority = 1000; - strStatusBar = strMiscWarning; - } - - // Longer invalid proof-of-work chain - if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) - { - nPriority = 2000; - strStatusBar = strRPC = _("Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."); - } - - // ppcoin: if detected invalid checkpoint enter safe mode - if (hashInvalidCheckpoint != 0) - { - nPriority = 3000; - strStatusBar = strRPC = "WARNING: Inconsistent checkpoint found! Stop enforcing checkpoints and notify developers to resolve the issue."; - } - - // Alerts - { - LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - { - const CAlert& alert = item.second; - if (alert.AppliesToMe() && alert.nPriority > nPriority) - { - nPriority = alert.nPriority; - strStatusBar = alert.strStatusBar; - } - } - } - - if (strFor == "statusbar") - return strStatusBar; - else if (strFor == "rpc") - return strRPC; - assert(!"GetWarnings() : invalid parameter"); - return "error"; -} - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Messages -// - - -bool static AlreadyHave(const CInv& inv) -{ - switch (inv.type) - { - case MSG_TX: - { - bool txInMap = false; - { - LOCK(mempool.cs); - txInMap = mempool.exists(inv.hash); - } - return txInMap || mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); - } - case MSG_BLOCK: - return mapBlockIndex.count(inv.hash) || - mapOrphanBlocks.count(inv.hash); - } - // Don't know what it is, just say we already got one - return true; -} - - - - -// The message start string is designed to be unlikely to occur in normal data. -// The characters are rarely used upper ASCII, not valid as UTF-8, and produce -// a large 4-byte int at any alignment. -unsigned char pchMessageStart[4] = { 0xe4, 0xe7, 0xe5, 0xe7 }; - - -void static ProcessGetData(CNode* pfrom) -{ - std::deque::iterator it = pfrom->vRecvGetData.begin(); - - vector vNotFound; - - while (it != pfrom->vRecvGetData.end()) { - // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= SendBufferSize()) - break; - - const CInv &inv = *it; - { - boost::this_thread::interruption_point(); - it++; - - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) - { - // Send block from disk - map::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) - { - CBlock block; - block.ReadFromDisk((*mi).second); - if (inv.type == MSG_BLOCK) - pfrom->PushMessage("block", block); - else // MSG_FILTERED_BLOCK) - { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) - { - CMerkleBlock merkleBlock(block, *pfrom->pfilter); - pfrom->PushMessage("merkleblock", merkleBlock); - // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see - // This avoids hurting performance by pointlessly requiring a round-trip - // Note that there is currently no way for a node to request any single transactions we didnt send here - - // they must either disconnect and retry or request the full block. - // Thus, the protocol spec specified allows for us to provide duplicate txn here, - // however we MUST always provide at least what the remote peer needs - typedef std::pair PairType; - BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) - if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) - pfrom->PushMessage("tx", block.vtx[pair.first]); - } - // else - // no response - } - - // Trigger them to send a getblocks request for the next batch of inventory - if (inv.hash == pfrom->hashContinue) - { - // Bypass PushInventory, this must send even if redundant, - // and we want it right after the last block so they don't - // wait for other stuff first. - vector vInv; - vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); - pfrom->PushMessage("inv", vInv); - pfrom->hashContinue = 0; - } - } - } - else if (inv.IsKnownType()) - { - // Send stream from relay memory - bool pushed = false; - { - LOCK(cs_mapRelay); - map::iterator mi = mapRelay.find(inv); - if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); - pushed = true; - } - } - if (!pushed && inv.type == MSG_TX) { - LOCK(mempool.cs); - if (mempool.exists(inv.hash)) { - CTransaction tx = mempool.lookup(inv.hash); - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << tx; - pfrom->PushMessage("tx", ss); - pushed = true; - } - } - if (!pushed) { - vNotFound.push_back(inv); - } - } - - // Track requests for our stuff. - Inventory(inv.hash); - } - } - - pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); - - if (!vNotFound.empty()) { - // Let the peer know that we didn't find what it asked for, so it doesn't - // have to wait around forever. Currently only SPV clients actually care - // about this message: it's needed when they are recursively walking the - // dependencies of relevant unconfirmed transactions. SPV clients want to - // do that because they want to know about (and store and rebroadcast and - // risk analyze) the dependencies of transactions relevant to them, without - // having to download the entire memory pool. - pfrom->PushMessage("notfound", vNotFound); - } -} - -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) -{ - RandAddSeedPerfmon(); - if (fDebug) - printf("received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - printf("dropmessagestest DROPPING RECV MESSAGE\n"); - return true; - } - - - - - - if (strCommand == "version") - { - // Each connection can only send one version message - if (pfrom->nVersion != 0) - { - pfrom->Misbehaving(1); - return false; - } - - int64 nTime; - CAddress addrMe; - CAddress addrFrom; - uint64 nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; - if (pfrom->nVersion < MIN_PROTO_VERSION) - { - // Since February 20, 2012, the protocol is initiated at version 209, - // and earlier versions are no longer supported - printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); - pfrom->fDisconnect = true; - return false; - } - - if (pfrom->nVersion == 10300) - pfrom->nVersion = 300; - if (!vRecv.empty()) - vRecv >> addrFrom >> nNonce; - if (!vRecv.empty()) - vRecv >> pfrom->strSubVer; - if (!vRecv.empty()) - vRecv >> pfrom->nStartingHeight; - if (!vRecv.empty()) - vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message - else - pfrom->fRelayTxes = true; - - if (pfrom->fInbound && addrMe.IsRoutable()) - { - pfrom->addrLocal = addrMe; - SeenLocal(addrMe); - } - - // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce && nNonce > 1) - { - printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); - pfrom->fDisconnect = true; - return true; - } - - // Be shy and don't send version until we hear - if (pfrom->fInbound) - pfrom->PushVersion(); - - pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - - AddTimeData(pfrom->addr, nTime); - - // Change version - pfrom->PushMessage("verack"); - pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - - if (!pfrom->fInbound) - { - // Advertise our address - if (!fNoListen && !IsInitialBlockDownload()) - { - CAddress addr = GetLocalAddress(&pfrom->addr); - if (addr.IsRoutable()) - pfrom->PushAddress(addr); - } - - // Get recent addresses - if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) - { - pfrom->PushMessage("getaddr"); - pfrom->fGetAddr = true; - } - addrman.Good(pfrom->addr); - } else { - if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) - { - addrman.Add(addrFrom, addrFrom); - addrman.Good(addrFrom); - } - } - - // Relay alerts - { - LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); - } - - // ppcoin: relay sync-checkpoint - { - LOCK(cs_hashSyncCheckpoint); - if (!checkpointMessage.IsNull()) - checkpointMessage.RelayTo(pfrom); - } - - pfrom->fSuccessfullyConnected = true; - - printf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); - - cPeerBlockCounts.input(pfrom->nStartingHeight); - - // ppcoin: ask for pending sync-checkpoint if any - if (!IsInitialBlockDownload()) - AskForPendingSyncCheckpoint(pfrom); - } - - - else if (pfrom->nVersion == 0) - { - // Must have a version message before anything else - pfrom->Misbehaving(1); - return false; - } - - - else if (strCommand == "verack") - { - pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - } - - - else if (strCommand == "addr") - { - vector vAddr; - vRecv >> vAddr; - - // Don't want addr from older versions unless seeding - if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) - return true; - if (vAddr.size() > 1000) - { - pfrom->Misbehaving(20); - return error("message addr size() = %"PRIszu"", vAddr.size()); - } - - // Store the new addresses - vector vAddrOk; - int64 nNow = GetAdjustedTime(); - int64 nSince = nNow - 10 * 60; - BOOST_FOREACH(CAddress& addr, vAddr) - { - boost::this_thread::interruption_point(); - - if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) - addr.nTime = nNow - 5 * 24 * 60 * 60; - pfrom->AddAddressKnown(addr); - bool fReachable = IsReachable(addr); - if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) - { - // Relay to a limited number of other nodes - { - LOCK(cs_vNodes); - // Use deterministic randomness to send to the same nodes for 24 hours - // at a time so the setAddrKnowns of the chosen nodes prevent repeats - static uint256 hashSalt; - if (hashSalt == 0) - hashSalt = GetRandHash(); - uint64 hashAddr = addr.GetHash(); - uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - multimap mapMix; - BOOST_FOREACH(CNode* pnode, vNodes) - { - if (pnode->nVersion < CADDR_TIME_VERSION) - continue; - unsigned int nPointer; - memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = hashRand ^ nPointer; - hashKey = Hash(BEGIN(hashKey), END(hashKey)); - mapMix.insert(make_pair(hashKey, pnode)); - } - int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) - for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) - ((*mi).second)->PushAddress(addr); - } - } - // Do not store addresses outside our network - if (fReachable) - vAddrOk.push_back(addr); - } - addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); - if (vAddr.size() < 1000) - pfrom->fGetAddr = false; - if (pfrom->fOneShot) - pfrom->fDisconnect = true; - } - - - else if (strCommand == "inv") - { - vector vInv; - vRecv >> vInv; - if (vInv.size() > MAX_INV_SZ) - { - pfrom->Misbehaving(20); - return error("message inv size() = %"PRIszu"", vInv.size()); - } - - // find last block in inv vector - unsigned int nLastBlock = (unsigned int)(-1); - for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { - if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { - nLastBlock = vInv.size() - 1 - nInv; - break; - } - } - for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) - { - const CInv &inv = vInv[nInv]; - - boost::this_thread::interruption_point(); - pfrom->AddInventoryKnown(inv); - - bool fAlreadyHave = AlreadyHave(inv); - if (fDebug) - printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); - - if (!fAlreadyHave) { - if (!fImporting && !fReindex) - pfrom->AskFor(inv); - } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { - pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); - } else if (nInv == nLastBlock) { - // In case we are on a very long side-chain, it is possible that we already have - // the last block in an inv bundle sent in response to getblocks. Try to detect - // this situation and push another getblocks to continue. - pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); - if (fDebug) - printf("force request: %s\n", inv.ToString().c_str()); - } - - // Track requests for our stuff - Inventory(inv.hash); - } - } - - - else if (strCommand == "getdata") - { - vector vInv; - vRecv >> vInv; - if (vInv.size() > MAX_INV_SZ) - { - pfrom->Misbehaving(20); - return error("message getdata size() = %"PRIszu"", vInv.size()); - } - - if (fDebugNet || (vInv.size() != 1)) - printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); - - if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1)) - printf("received getdata for: %s\n", vInv[0].ToString().c_str()); - - pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom); - } - - - else if (strCommand == "getblocks") - { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - // Find the last block the caller has in the main chain - CBlockIndex* pindex = locator.GetBlockIndex(); - - // Send the rest of the chain - if (pindex) - pindex = pindex->pnext; - int nLimit = 500; - printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); - for (; pindex; pindex = pindex->pnext) - { - if (pindex->GetBlockHash() == hashStop) - { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - break; - } - pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); - if (--nLimit <= 0) - { - // When this block is requested, we'll send an inv that'll make them - // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - pfrom->hashContinue = pindex->GetBlockHash(); - break; - } - } - } - - - else if (strCommand == "getheaders") - { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - CBlockIndex* pindex = NULL; - if (locator.IsNull()) - { - // If locator is null, return the hashStop block - map::iterator mi = mapBlockIndex.find(hashStop); - if (mi == mapBlockIndex.end()) - return true; - pindex = (*mi).second; - } - else - { - // Find the last block the caller has in the main chain - pindex = locator.GetBlockIndex(); - if (pindex) - pindex = pindex->pnext; - } - - // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end - vector vHeaders; - int nLimit = 2000; - printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); - for (; pindex; pindex = pindex->pnext) - { - vHeaders.push_back(pindex->GetBlockHeader()); - if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) - break; - } - pfrom->PushMessage("headers", vHeaders); - } - - - else if (strCommand == "tx") - { - vector vWorkQueue; - vector vEraseQueue; - CDataStream vMsg(vRecv); - CTransaction tx; - vRecv >> tx; - - CInv inv(MSG_TX, tx.GetHash()); - pfrom->AddInventoryKnown(inv); - - // Truncate messages to the size of the tx in them - unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - unsigned int oldSize = vMsg.size(); - if (nSize < oldSize) { - vMsg.resize(nSize); - printf("truncating oversized TX %s (%u -> %u)\n", - tx.GetHash().ToString().c_str(), - oldSize, nSize); - } - - bool fMissingInputs = false; - CValidationState state; - if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) - { - RelayTransaction(tx, inv.hash, vMsg); - mapAlreadyAskedFor.erase(inv); - vWorkQueue.push_back(inv.hash); - vEraseQueue.push_back(inv.hash); - - // Recursively process any orphan transactions that depended on this one - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hashPrev = vWorkQueue[i]; - for (map::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); - mi != mapOrphanTransactionsByPrev[hashPrev].end(); - ++mi) - { - const CDataStream& vMsg = *((*mi).second); - CTransaction tx; - CDataStream(vMsg) >> tx; - CInv inv(MSG_TX, tx.GetHash()); - bool fMissingInputs2 = false; - // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) - CValidationState stateDummy; - - if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) - { - printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str()); - RelayTransaction(tx, inv.hash, vMsg); - mapAlreadyAskedFor.erase(inv); - vWorkQueue.push_back(inv.hash); - vEraseQueue.push_back(inv.hash); - } - else if (!fMissingInputs2) - { - // invalid or too-little-fee orphan - vEraseQueue.push_back(inv.hash); - printf(" removed orphan tx %s\n", inv.hash.ToString().c_str()); - } - } - } - - BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); - } - else if (fMissingInputs) - { - AddOrphanTx(vMsg); - - // DoS prevention: do not allow mapOrphanTransactions to grow unbounded - unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); - if (nEvicted > 0) - printf("mapOrphan overflow, removed %u tx\n", nEvicted); - } - int nDoS; - if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); - } - - - else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing - { - CBlock block; - vRecv >> block; - - printf("received block %s\n", block.GetHash().ToString().c_str()); - // block.print(); - - CInv inv(MSG_BLOCK, block.GetHash()); - pfrom->AddInventoryKnown(inv); - - CValidationState state; - if (ProcessBlock(state, pfrom, &block)) - mapAlreadyAskedFor.erase(inv); - int nDoS; - if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); - } - - - else if (strCommand == "getaddr") - { - pfrom->vAddrToSend.clear(); - vector vAddr = addrman.GetAddr(); - BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); - } - - - else if (strCommand == "mempool") - { - std::vector vtxid; - LOCK2(mempool.cs, pfrom->cs_filter); - mempool.queryHashes(vtxid); - vector vInv; - BOOST_FOREACH(uint256& hash, vtxid) { - CInv inv(MSG_TX, hash); - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || - (!pfrom->pfilter)) - vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) - break; - } - if (vInv.size() > 0) - pfrom->PushMessage("inv", vInv); - } - - - else if (strCommand == "ping") - { - if (pfrom->nVersion > BIP0031_VERSION) - { - uint64 nonce = 0; - vRecv >> nonce; - // Echo the message back with the nonce. This allows for two useful features: - // - // 1) A remote node can quickly check if the connection is operational - // 2) Remote nodes can measure the latency of the network thread. If this node - // is overloaded it won't respond to pings quickly and the remote node can - // avoid sending us more work, like chain download requests. - // - // The nonce stops the remote getting confused between different pings: without - // 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); - } - } - - - else if (strCommand == "alert") - { - CAlert alert; - vRecv >> alert; - - uint256 alertHash = alert.GetHash(); - if (pfrom->setKnown.count(alertHash) == 0) - { - if (alert.ProcessAlert()) - { - // Relay - pfrom->setKnown.insert(alertHash); - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); - } - } - else { - // Small DoS penalty so peers that send us lots of - // duplicate/expired/invalid-signature/whatever alerts - // eventually get banned. - // This isn't a Misbehaving(100) (immediate ban) because the - // peer might be an older or different implementation with - // a different signature key, etc. - pfrom->Misbehaving(10); - } - } - } - - - else if (strCommand == "checkpoint") // ppcoin synchronized checkpoint - { - CSyncCheckpoint checkpoint; - vRecv >> checkpoint; - - if (checkpoint.ProcessSyncCheckpoint(pfrom)) - { - // Relay - pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint; - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - checkpoint.RelayTo(pnode); - } - } - - - else if (strCommand == "filterload") - { - CBloomFilter filter; - vRecv >> filter; - - if (!filter.IsWithinSizeConstraints()) - // There is no excuse for sending a too-large filter - pfrom->Misbehaving(100); - else - { - LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(filter); - } - pfrom->fRelayTxes = true; - } - - - else if (strCommand == "filteradd") - { - vector vData; - vRecv >> vData; - - // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, - // and thus, the maximum size any matched object can have) in a filteradd message - if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) - { - pfrom->Misbehaving(100); - } else { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) - pfrom->pfilter->insert(vData); - else - pfrom->Misbehaving(100); - } - } - - - else if (strCommand == "filterclear") - { - LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = NULL; - pfrom->fRelayTxes = true; - } - - - 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; -} - -// requires LOCK(cs_vRecvMsg) -bool ProcessMessages(CNode* pfrom) -{ - //if (fDebug) - // printf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); - - // - // Message format - // (4) message start - // (12) command - // (4) size - // (4) checksum - // (x) data - // - bool fOk = true; - - if (!pfrom->vRecvGetData.empty()) - ProcessGetData(pfrom); - - std::deque::iterator it = pfrom->vRecvMsg.begin(); - while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { - // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= SendBufferSize()) - break; - - // get next message - CNetMessage& msg = *it; - - //if (fDebug) - // printf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", - // msg.hdr.nMessageSize, msg.vRecv.size(), - // msg.complete() ? "Y" : "N"); - - // end, if an incomplete message is found - if (!msg.complete()) - break; - - // at this point, any failure means we can delete the current message - it++; - - // Scan for message start - if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { - printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); - fOk = false; - break; - } - - // Read header - CMessageHeader& hdr = msg.hdr; - if (!hdr.IsValid()) - { - printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); - continue; - } - string strCommand = hdr.GetCommand(); - - // Message size - unsigned int nMessageSize = hdr.nMessageSize; - - // Checksum - CDataStream& vRecv = msg.vRecv; - uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - if (nChecksum != hdr.nChecksum) - { - printf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); - continue; - } - - // Process message - bool fRet = false; - try - { - { - LOCK(cs_main); - fRet = ProcessMessage(pfrom, strCommand, vRecv); - } - boost::this_thread::interruption_point(); - } - catch (std::ios_base::failure& e) - { - if (strstr(e.what(), "end of data")) - { - // Allow exceptions from under-length message on vRecv - printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); - } - else if (strstr(e.what(), "size too large")) - { - // Allow exceptions from over-long size - printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); - } - else - { - PrintExceptionContinue(&e, "ProcessMessages()"); - } - } - catch (boost::thread_interrupted) { - throw; - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ProcessMessages()"); - } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessages()"); - } - - if (!fRet) - printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); - } - - // In case the connection got shut down, its receive buffer was wiped - if (!pfrom->fDisconnect) - pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - - return fOk; -} - - -bool SendMessages(CNode* pto, bool fSendTrickle) -{ - TRY_LOCK(cs_main, lockMain); - if (lockMain) { - // Don't send anything until we get their version message - if (pto->nVersion == 0) - return true; - - // Keep-alive ping. We send a nonce of zero because we don't use it anywhere - // right now. - if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) { - uint64 nonce = 0; - if (pto->nVersion > BIP0031_VERSION) - pto->PushMessage("ping", nonce); - else - pto->PushMessage("ping"); - } - - // Start block sync - if (pto->fStartSync && !fImporting && !fReindex) { - pto->fStartSync = false; - pto->PushGetBlocks(pindexBest, uint256(0)); - } - - // Resend wallet transactions that haven't gotten in a block yet - // Except during reindex, importing and IBD, when old wallet - // transactions become unconfirmed and spams other nodes. - if (!fReindex && !fImporting && !IsInitialBlockDownload()) - { - ResendWalletTransactions(); - } - - // Address refresh broadcast - static int64 nLastRebroadcast; - if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) - { - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { - // Periodically clear setAddrKnown to allow refresh broadcasts - if (nLastRebroadcast) - pnode->setAddrKnown.clear(); - - // Rebroadcast our address - if (!fNoListen) - { - CAddress addr = GetLocalAddress(&pnode->addr); - if (addr.IsRoutable()) - pnode->PushAddress(addr); - } - } - } - nLastRebroadcast = GetTime(); - } - - // - // Message: addr - // - if (fSendTrickle) - { - vector vAddr; - vAddr.reserve(pto->vAddrToSend.size()); - BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) - { - // returns true if wasn't already contained in the set - if (pto->setAddrKnown.insert(addr).second) - { - vAddr.push_back(addr); - // receiver rejects addr messages larger than 1000 - if (vAddr.size() >= 1000) - { - pto->PushMessage("addr", vAddr); - vAddr.clear(); - } - } - } - pto->vAddrToSend.clear(); - if (!vAddr.empty()) - pto->PushMessage("addr", vAddr); - } - - - // - // Message: inventory - // - vector vInv; - vector vInvWait; - { - LOCK(pto->cs_inventory); - vInv.reserve(pto->vInventoryToSend.size()); - vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) - { - if (pto->setInventoryKnown.count(inv)) - continue; - - // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) - { - // 1/4 of tx invs blast to all immediately - static uint256 hashSalt; - if (hashSalt == 0) - hashSalt = GetRandHash(); - uint256 hashRand = inv.hash ^ hashSalt; - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((hashRand & 3) != 0); - - // always trickle our own transactions - if (!fTrickleWait) - { - CWalletTx wtx; - if (GetTransaction(inv.hash, wtx)) - if (wtx.fFromMe) - fTrickleWait = true; - } - - if (fTrickleWait) - { - vInvWait.push_back(inv); - continue; - } - } - - // returns true if wasn't already contained in the set - if (pto->setInventoryKnown.insert(inv).second) - { - vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage("inv", vInv); - vInv.clear(); - } - } - } - pto->vInventoryToSend = vInvWait; - } - if (!vInv.empty()) - pto->PushMessage("inv", vInv); - - - // - // Message: getdata - // - vector vGetData; - int64 nNow = GetTime() * 1000000; - while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) - { - const CInv& inv = (*pto->mapAskFor.begin()).second; - if (!AlreadyHave(inv)) - { - if (fDebugNet) - printf("sending getdata: %s\n", inv.ToString().c_str()); - vGetData.push_back(inv); - if (vGetData.size() >= 1000) - { - pto->PushMessage("getdata", vGetData); - vGetData.clear(); - } - } - pto->mapAskFor.erase(pto->mapAskFor.begin()); - } - if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); - - } - return true; -} - - - - - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// BitcoinMiner -// - -int FormatHashBlocks(void* pbuffer, unsigned int len) -{ - unsigned char* pdata = (unsigned char*)pbuffer; - unsigned int blocks = 1 + ((len + 8) / 64); - unsigned char* pend = pdata + 64 * blocks; - memset(pdata + len, 0, 64 * blocks - len); - pdata[len] = 0x80; - unsigned int bits = len * 8; - pend[-1] = (bits >> 0) & 0xff; - pend[-2] = (bits >> 8) & 0xff; - pend[-3] = (bits >> 16) & 0xff; - pend[-4] = (bits >> 24) & 0xff; - return blocks; -} - -static const unsigned int pSHA256InitState[8] = -{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -void SHA256Transform(void* pstate, void* pinput, const void* pinit) -{ - SHA256_CTX ctx; - unsigned char data[64]; - - SHA256_Init(&ctx); - - for (int i = 0; i < 16; i++) - ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); - - for (int i = 0; i < 8; i++) - ctx.h[i] = ((uint32_t*)pinit)[i]; - - SHA256_Update(&ctx, data, sizeof(data)); - for (int i = 0; i < 8; i++) - ((uint32_t*)pstate)[i] = ctx.h[i]; -} - -// -// ScanHash scans nonces looking for a hash with at least some zero bits. -// It operates on big endian data. Caller does the byte reversing. -// All input buffers are 16-byte aligned. nNonce is usually preserved -// between calls, but periodically or if nNonce is 0xffff0000 or above, -// the block is rebuilt and nNonce starts over at zero. -// -// Primecoin: ScanHash is not needed for primecoin - - -// Some explaining would be appreciated -class COrphan -{ -public: - CTransaction* ptx; - set setDependsOn; - double dPriority; - double dFeePerKb; - - COrphan(CTransaction* ptxIn) - { - ptx = ptxIn; - dPriority = dFeePerKb = 0; - } - - void print() const - { - printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", - ptx->GetHash().ToString().c_str(), dPriority, dFeePerKb); - BOOST_FOREACH(uint256 hash, setDependsOn) - printf(" setDependsOn %s\n", hash.ToString().c_str()); - } -}; - - -uint64 nLastBlockTx = 0; -uint64 nLastBlockSize = 0; - -// We want to sort transactions by priority and fee, so: -typedef boost::tuple TxPriority; -class TxPriorityCompare -{ - bool byFee; -public: - TxPriorityCompare(bool _byFee) : byFee(_byFee) { } - bool operator()(const TxPriority& a, const TxPriority& b) - { - if (byFee) - { - if (a.get<1>() == b.get<1>()) - return a.get<0>() < b.get<0>(); - return a.get<1>() < b.get<1>(); - } - else - { - if (a.get<0>() == b.get<0>()) - return a.get<1>() < b.get<1>(); - return a.get<0>() < b.get<0>(); - } - } -}; - -CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) -{ - // Create new block - auto_ptr pblocktemplate(new CBlockTemplate()); - if(!pblocktemplate.get()) - return NULL; - CBlock *pblock = &pblocktemplate->block; // pointer for convenience - - // Create coinbase tx - CTransaction txNew; - txNew.vin.resize(1); - txNew.vin[0].prevout.SetNull(); - txNew.vout.resize(1); - CPubKey pubkey; - if (!reservekey.GetReservedKey(pubkey)) - return NULL; - txNew.vout[0].scriptPubKey << pubkey << OP_CHECKSIG; - - // Add our coinbase tx as first transaction - pblock->vtx.push_back(txNew); - pblocktemplate->vTxFees.push_back(-1); // updated at end - pblocktemplate->vTxSigOps.push_back(-1); // updated at end - - // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); - // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); - - // Special compatibility rule before 15 May: limit size to 500,000 bytes: - if (GetAdjustedTime() < 1368576000) - nBlockMaxSize = std::min(nBlockMaxSize, (unsigned int)(MAX_BLOCK_SIZE_GEN)); - - // How much of the block should be dedicated to high-priority transactions, - // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); - nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); - - // Minimum block size you want to create; block will be filled with free transactions - // until there are no more or the block reaches this size: - unsigned int nBlockMinSize = GetArg("-blockminsize", 0); - nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); - - // Collect memory pool transactions into the block - int64 nFees = 0; - { - LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = pindexBest; - CCoinsViewCache view(*pcoinsTip, true); - - // Priority order to process transactions - list vOrphan; // list memory doesn't move - map > mapDependers; - bool fPrintPriority = GetBoolArg("-printpriority"); - - // This vector will be sorted into a priority queue: - vector vecPriority; - vecPriority.reserve(mempool.mapTx.size()); - for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) - { - CTransaction& tx = (*mi).second; - if (tx.IsCoinBase() || !tx.IsFinal()) - continue; - - COrphan* porphan = NULL; - double dPriority = 0; - int64 nTotalIn = 0; - bool fMissingInputs = false; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - // Read prev transaction - if (!view.HaveCoins(txin.prevout.hash)) - { - // This should never happen; all transactions in the memory - // pool should connect to either transactions in the chain - // or other transactions in the memory pool. - if (!mempool.mapTx.count(txin.prevout.hash)) - { - printf("ERROR: mempool transaction missing input\n"); - if (fDebug) assert("mempool transaction missing input" == 0); - fMissingInputs = true; - if (porphan) - vOrphan.pop_back(); - break; - } - - // Has to wait for dependencies - if (!porphan) - { - // Use list for automatic deletion - vOrphan.push_back(COrphan(&tx)); - porphan = &vOrphan.back(); - } - mapDependers[txin.prevout.hash].push_back(porphan); - porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; - continue; - } - const CCoins &coins = view.GetCoins(txin.prevout.hash); - - int64 nValueIn = coins.vout[txin.prevout.n].nValue; - nTotalIn += nValueIn; - - int nConf = pindexPrev->nHeight - coins.nHeight + 1; - - dPriority += (double)nValueIn * nConf; - } - if (fMissingInputs) continue; - - // Priority is sum(valuein * age) / txsize - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - dPriority /= nTxSize; - - // This is a more accurate fee-per-kilobyte than is used by the client code, because the - // client code rounds up the size to the nearest 1K. That's good, because it gives an - // incentive to create smaller transactions. - double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); - - if (porphan) - { - porphan->dPriority = dPriority; - porphan->dFeePerKb = dFeePerKb; - } - else - vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); - } - - // Collect transactions into block - uint64 nBlockSize = 1000; - uint64 nBlockTx = 0; - int nBlockSigOps = 100; - bool fSortedByFee = (nBlockPrioritySize <= 0); - - TxPriorityCompare comparer(fSortedByFee); - std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); - - while (!vecPriority.empty()) - { - // Take highest priority transaction off the priority queue: - double dPriority = vecPriority.front().get<0>(); - double dFeePerKb = vecPriority.front().get<1>(); - CTransaction& tx = *(vecPriority.front().get<2>()); - - std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); - vecPriority.pop_back(); - - // Size limits - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - if (nBlockSize + nTxSize >= nBlockMaxSize) - continue; - - // Legacy limits on sigOps: - unsigned int nTxSigOps = tx.GetLegacySigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; - - // Skip free transactions if we're past the minimum block size: - if (fSortedByFee && (dFeePerKb < CTransaction::nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) - continue; - - // Prioritize by fee once past the priority size or we run out of high-priority - // transactions: - if (!fSortedByFee && - ((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 144 / 250))) - { - fSortedByFee = true; - comparer = TxPriorityCompare(fSortedByFee); - std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); - } - - if (!tx.HaveInputs(view)) - continue; - - int64 nTxFees = tx.GetValueIn(view)-tx.GetValueOut(); - - nTxSigOps += tx.GetP2SHSigOpCount(view); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; - - CValidationState state; - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) - continue; - - CTxUndo txundo; - uint256 hash = tx.GetHash(); - tx.UpdateCoins(state, view, txundo, pindexPrev->nHeight+1, hash); - - // Added - pblock->vtx.push_back(tx); - pblocktemplate->vTxFees.push_back(nTxFees); - pblocktemplate->vTxSigOps.push_back(nTxSigOps); - nBlockSize += nTxSize; - ++nBlockTx; - nBlockSigOps += nTxSigOps; - nFees += nTxFees; - - if (fPrintPriority) - { - printf("priority %.1f feeperkb %.1f txid %s\n", - dPriority, dFeePerKb, tx.GetHash().ToString().c_str()); - } - - // Add transactions that depend on this one to the priority queue - if (mapDependers.count(hash)) - { - BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) - { - if (!porphan->setDependsOn.empty()) - { - porphan->setDependsOn.erase(hash); - if (porphan->setDependsOn.empty()) - { - vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx)); - std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); - } - } - } - } - } - - nLastBlockTx = nBlockTx; - nLastBlockSize = nBlockSize; - if (fDebug && GetBoolArg("-printmining")) - printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); - pblock->vtx[0].vout[0].nValue = GetBlockValue(pblock->nBits, nFees); - pblocktemplate->vTxFees[0] = -nFees; - - // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->UpdateTime(pindexPrev); - pblock->nNonce = 0; - pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; - pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); - - CBlockIndex indexDummy(*pblock); - indexDummy.pprev = pindexPrev; - indexDummy.nHeight = pindexPrev->nHeight + 1; - CCoinsViewCache viewNew(*pcoinsTip, true); - CValidationState state; - if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) - throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); - } - - return pblocktemplate.release(); -} - - -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) -{ - // Update nExtraNonce - static uint256 hashPrevBlock; - if (hashPrevBlock != pblock->hashPrevBlock) - { - nExtraNonce = 0; - hashPrevBlock = pblock->hashPrevBlock; - } - ++nExtraNonce; - unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 - const char* pszDedication = mapArgs.count("-dedication")? mapArgs["-dedication"].c_str() : ""; - pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce) << vector((const unsigned char*)pszDedication, (const unsigned char*)pszDedication + strlen(pszDedication))) + COINBASE_FLAGS; - assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); - - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); -} - - -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) -{ - // - // Pre-build hash buffers - // - struct - { - struct unnamed2 - { - int nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - } - block; - unsigned char pchPadding0[64]; - uint256 hash1; - unsigned char pchPadding1[64]; - } - tmp; - memset(&tmp, 0, sizeof(tmp)); - - tmp.block.nVersion = pblock->nVersion; - tmp.block.hashPrevBlock = pblock->hashPrevBlock; - tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; - tmp.block.nTime = pblock->nTime; - tmp.block.nBits = pblock->nBits; - tmp.block.nNonce = pblock->nNonce; - - FormatHashBlocks(&tmp.block, sizeof(tmp.block)); - FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); - - // Byte swap all the input buffer - for (unsigned int i = 0; i < sizeof(tmp)/4; i++) - ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); - - // Precalc the first half of the first hash, which stays constant - SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); - - memcpy(pdata, &tmp.block, 128); - memcpy(phash1, &tmp.hash1, 64); -} - - -bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) -{ - CBigNum bnTarget = CBigNum().SetCompact(pblock->nBits); - - if (!CheckProofOfWork(pblock->GetHeaderHash(), pblock->nBits, pblock->bnPrimeChainMultiplier, pblock->nPrimeChainType, pblock->nPrimeChainLength)) - return error("PrimecoinMiner : failed proof-of-work check"); - - //// debug print - printf("PrimecoinMiner:\n"); - printf("proof-of-work found \n target: %s\n multiplier: %s\n ", TargetToString(pblock->nBits).c_str(), pblock->bnPrimeChainMultiplier.GetHex().c_str()); - pblock->print(); - printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); - - // Found a solution - { - LOCK(cs_main); - if (pblock->hashPrevBlock != hashBestChain) - return error("PrimecoinMiner : generated block is stale"); - - // Remove key from key pool - reservekey.KeepKey(); - - // Track how many getdata requests this block gets - { - LOCK(wallet.cs_wallet); - wallet.mapRequestCount[pblock->GetHash()] = 0; - } - - // Process this block the same as if we had received it from another node - CValidationState state; - if (!ProcessBlock(state, NULL, pblock)) - return error("PrimecoinMiner : ProcessBlock, block not accepted"); - } - - return true; -} - -#include "json/json_spirit_value.h" - -void BitcoinMiner(CWallet *pwallet, CBlockProvider *block_provider, unsigned int thread_id); - -void BitcoinMinerWallet(CWallet *pwallet) -{ - BitcoinMiner(pwallet, NULL, 0); -} - -void BitcoinMiner(CWallet *pwallet, CBlockProvider *block_provider, unsigned int thread_id) -{ - printf("PrimecoinMiner started\n"); - SetThreadPriority(THREAD_PRIORITY_LOWEST); - RenameThread("primecoin-miner"); - - // Each thread has its own kcd ey and counter - CReserveKey reservekey(pwallet); //will be ignored when pool-mining - unsigned int nExtraNonce = 0; //^ - - unsigned int nPrimorialMultiplier = nPrimorialHashFactor; - double dTimeExpected = 0; // time expected to prime chain (micro-second) - int64 nSieveGenTime = 0; // how many milliseconds sieve generation took - bool fIncrementPrimorial = true; // increase or decrease primorial factor - - CBlock *pblock = NULL; - uint256 old_hash; - unsigned int old_nonce = 0; - - try { loop { - while (block_provider == NULL && vNodes.empty()) - MilliSleep(1000); - - // - // Create new block - // - unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; - - auto_ptr pblocktemplate; - if (block_provider == NULL) { - pblocktemplate = auto_ptr(CreateNewBlock(reservekey)); - if (!pblocktemplate.get()) - return; - pblock = &pblocktemplate->block; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - } else if ((pblock = block_provider->getBlock(thread_id, pblock == NULL ? 0 : pblock->nTime)) == NULL) { //server not reachable? - MilliSleep(20000); - continue; - } else if (old_hash == pblock->GetHeaderHash()) { - if (old_nonce >= 0xffff0000) { - MilliSleep(100); - //TODO: FORCE a new getblock! - if (fDebug && GetBoolArg("-printmining")) - printf("Nothing to do --- uh ih uh ah ah bing bang!!\n"); - continue; - } else - pblock->nNonce = old_nonce; - } else { - old_hash = pblock->GetHeaderHash(); - old_nonce = 0; - } - - if (fDebug && GetBoolArg("-printmining")) - printf("Running PrimecoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), - ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); - - // - // Search - // - int64 nStart = GetTime(); - bool fNewBlock = true; - unsigned int nTriedMultiplier = 0; - - // Primecoin: try to find hash divisible by primorial - unsigned int nHashFactor = PrimorialFast(nPrimorialHashFactor); - - // Based on mustyoshi's patch from https://bitcointalk.org/index.php?topic=251850.msg2689981#msg2689981 - uint256 phash; - mpz_class mpzHash; - loop { - // Fast loop - if (pblock->nNonce >= 0xffff0000) - break; - - // Check that the hash meets the minimum - phash = pblock->GetHeaderHash(); - if (phash < hashBlockHeaderLimit) { - pblock->nNonce++; - continue; - } - - // Check that the hash is divisible by the fixed primorial - mpz_set_uint256(mpzHash.get_mpz_t(), phash); - if (!mpz_divisible_ui_p(mpzHash.get_mpz_t(), nHashFactor)) { - pblock->nNonce++; - continue; - } - - // Use the hash that passed the tests - break; - } - if (pblock->nNonce >= 0xffff0000) { - old_nonce = 0xffff0000; - continue; - } - // Primecoin: primorial fixed multiplier - mpz_class mpzPrimorial; - unsigned int nRoundTests = 0; - unsigned int nRoundPrimesHit = 0; - int64 nPrimeTimerStart = GetTimeMicros(); - Primorial(nPrimorialMultiplier, mpzPrimorial); - - loop - { - unsigned int nTests = 0; - unsigned int nPrimesHit = 0; - unsigned int nChainsHit = 0; - - // Primecoin: adjust round primorial so that the generated prime candidates meet the minimum - mpz_class mpzMultiplierMin = mpzPrimeMin * nHashFactor / mpzHash + 1; - while (mpzPrimorial < mpzMultiplierMin) - { - if (!PrimeTableGetNextPrime(nPrimorialMultiplier)) - error("PrimecoinMiner() : primorial minimum overflow"); - Primorial(nPrimorialMultiplier, mpzPrimorial); - } - mpz_class mpzFixedMultiplier; - if (mpzPrimorial > nHashFactor) { - mpzFixedMultiplier = mpzPrimorial / nHashFactor; - } else { - mpzFixedMultiplier = 1; - } - - // Primecoin: mine for prime chain - unsigned int nProbableChainLength; - if (MineProbablePrimeChain(*pblock, mpzFixedMultiplier, fNewBlock, nTriedMultiplier, nProbableChainLength, nTests, nPrimesHit, nChainsHit, mpzHash, nPrimorialMultiplier, nSieveGenTime, pindexPrev, block_provider != NULL)) - { - SetThreadPriority(THREAD_PRIORITY_NORMAL); - if (block_provider == NULL) - CheckWork(pblock, *pwalletMain, reservekey); - else - block_provider->submitBlock(pblock); - SetThreadPriority(THREAD_PRIORITY_LOWEST); - old_nonce = pblock->nNonce + 1; - break; - } - - /// - /// ENABLE the following code, if you need data for the perftool - /// - /*if (nProbableChainLength > 0 && nTests > 10) - { - static CCriticalSection cs; - { - LOCK(cs); - - std::ofstream output_file("miner_data"); - std::ofstream output_file_block("miner_data.blk", std::ofstream::out | std::ofstream::binary); - - ::Serialize(output_file_block, *pblock, 0, 0); //writeblock - - output_file << mpzFixedMultiplier.get_str(10) << std::endl; - output_file << fNewBlock << std::endl; - output_file << nTriedMultiplier << std::endl; - output_file << nPrimorialMultiplier << std::endl; - output_file << mpzHash.get_str(10) << std::endl; - - output_file.close(); - output_file_block.close(); - } - }*/ - - nRoundTests += nTests; - nRoundPrimesHit += nPrimesHit; - - // Meter primes/sec - static volatile int64 nPrimeCounter; - static volatile int64 nTestCounter; - static volatile int64 nChainCounter; - static double dChainExpected; - int64 nMillisNow = GetTimeMillis(); - if (nHPSTimerStart == 0) - { - nHPSTimerStart = nMillisNow; - nPrimeCounter = 0; - nTestCounter = 0; - nChainCounter = 0; - dChainExpected = 0; - } - else - { -#ifdef __GNUC__ - // Use atomic increment - __sync_add_and_fetch(&nPrimeCounter, nPrimesHit); - __sync_add_and_fetch(&nTestCounter, nTests); - __sync_add_and_fetch(&nChainCounter, nChainsHit); -#else - nPrimeCounter += nPrimesHit; - nTestCounter += nTests; - nChainCounter += nChainsHit; -#endif - } - if (nMillisNow - nHPSTimerStart > 60000) - { - static CCriticalSection cs; - { - LOCK(cs); - if (nMillisNow - nHPSTimerStart > 60000) - { - double dPrimesPerMinute = 60000.0 * nPrimeCounter / (nMillisNow - nHPSTimerStart); - dPrimesPerSec = dPrimesPerMinute / 60.0; - double dTestsPerSec = 1000.0 * nTestCounter / (nMillisNow - nHPSTimerStart); - dChainsPerMinute = 60000.0 * nChainCounter / (nMillisNow - nHPSTimerStart); - dChainsPerDay = 86400000.0 * dChainExpected / (GetTimeMillis() - nHPSTimerStart); - nHPSTimerStart = nMillisNow; - nPrimeCounter = 0; - nTestCounter = 0; - nChainCounter = 0; - dChainExpected = 0; - static int64 nLogTime = 0; - if (nMillisNow - nLogTime > 59000) - { - nLogTime = nMillisNow; - printf("[STATS] %s | %4.0f primes/s, %4.0f tests/s, %4.0f %d-chains/h, %3.3f chains/d\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nLogTime / 1000).c_str(), dPrimesPerSec, dTestsPerSec, dChainsPerMinute * 60.0, nStatsChainLength, dChainsPerDay); - } - } - } - } - - old_nonce = pblock->nNonce; - - // Check for stop or if block needs to be rebuilt - boost::this_thread::interruption_point(); - if (block_provider == NULL && vNodes.empty()) - break; - if (pblock->nNonce >= 0xffff0000) - break; - if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 10) - break; - if (pindexPrev != pindexBest/* || (block_provider != NULL && GetTime() - nStart > 200)*/) - break; - if (thread_id == 0 && block_provider != NULL && (GetTime() - nStart) > 300) { //5 minutes no update? something's wrong -> reconnect! - block_provider->forceReconnect(); - nStart = GetTime(); - } - if (fNewBlock) //aka: sieve's done, we need a updated nonce - { - // Primecoin: a sieve+primality round completes - // Primecoin: estimate time to block - const double dTimeExpectedPrev = dTimeExpected; - unsigned int nCalcRoundTests = max(1u, nRoundTests); - // Make sure the estimated time is very high if only 0 primes were found - if (nRoundPrimesHit == 0) - nCalcRoundTests *= 1000; - int64 nRoundTime = (GetTimeMicros() - nPrimeTimerStart); - dTimeExpected = (double) nRoundTime / nCalcRoundTests; - double dRoundChainExpected = (double) nRoundTests; - for (unsigned int n = 0, nTargetLength = TargetGetLength(pblock->nBits); n < nTargetLength; n++) - { - double dPrimeProbability = EstimateCandidatePrimeProbability(nPrimorialMultiplier, n); - dTimeExpected = dTimeExpected / max(0.01, dPrimeProbability); - dRoundChainExpected *= dPrimeProbability; - } - dChainExpected += dRoundChainExpected; - if (fDebug && GetBoolArg("-printmining")) - { - double dPrimeProbabilityBegin = EstimateCandidatePrimeProbability(nPrimorialMultiplier, 0); - unsigned int nTargetLength = TargetGetLength(pblock->nBits); - double dPrimeProbabilityEnd = EstimateCandidatePrimeProbability(nPrimorialMultiplier, nTargetLength - 1); - printf("PrimecoinMiner() : Round primorial=%u tests=%u primes=%u time=%uus pprob=%1.6f pprob2=%1.6f tochain=%6.3fd expect=%3.9f\n", nPrimorialMultiplier, nRoundTests, nRoundPrimesHit, (unsigned int) nRoundTime, dPrimeProbabilityBegin, dPrimeProbabilityEnd, ((dTimeExpected/1000000.0))/86400.0, dRoundChainExpected); - } - - // Primecoin: update time and nonce - //pblock->nTime = max(pblock->nTime, (unsigned int) GetAdjustedTime()); - pblock->nTime = max(pblock->nTime, block_provider->GetAdjustedTimeWithOffset(thread_id)); - pblock->nNonce++; - loop { - // Fast loop - if (pblock->nNonce >= 0xffff0000) - break; - - // Check that the hash meets the minimum - phash = pblock->GetHeaderHash(); - if (phash < hashBlockHeaderLimit) { - pblock->nNonce++; - continue; - } - - // Check that the hash is divisible by the fixed primorial - mpz_set_uint256(mpzHash.get_mpz_t(), phash); - if (!mpz_divisible_ui_p(mpzHash.get_mpz_t(), nHashFactor)) { - pblock->nNonce++; - continue; - } - - // Use the hash that passed the tests - break; - } - if (pblock->nNonce >= 0xffff0000) - break; - - // Primecoin: reset sieve+primality round timer - nPrimeTimerStart = GetTimeMicros(); - if (dTimeExpected > dTimeExpectedPrev) - fIncrementPrimorial = !fIncrementPrimorial; - - // Primecoin: primorial always needs to be incremented if only 0 primes were found - if (nRoundPrimesHit == 0) - fIncrementPrimorial = true; - - nRoundTests = 0; - nRoundPrimesHit = 0; - - // Primecoin: dynamic adjustment of primorial multiplier - if (fIncrementPrimorial) - { - if (!PrimeTableGetNextPrime(nPrimorialMultiplier)) - error("PrimecoinMiner() : primorial increment overflow"); - } - else if (nPrimorialMultiplier > nPrimorialHashFactor) - { - if (!PrimeTableGetPreviousPrime(nPrimorialMultiplier)) - error("PrimecoinMiner() : primorial decrement overflow"); - } - Primorial(nPrimorialMultiplier, mpzPrimorial); - } - } - } } - catch (boost::thread_interrupted) - { - printf("PrimecoinMiner terminated\n"); - throw; - } -} - -void GenerateBitcoins(bool fGenerate, CWallet* pwallet) -{ - static boost::thread_group* minerThreads = NULL; - - int nThreads = GetArg("-genproclimit", -1); - if (nThreads < 0) - nThreads = boost::thread::hardware_concurrency(); - - if (minerThreads != NULL) - { - minerThreads->interrupt_all(); - delete minerThreads; - minerThreads = NULL; - } - - if (nThreads == 0 || !fGenerate) - return; - - minerThreads = new boost::thread_group(); - for (int i = 0; i < nThreads; i++) - minerThreads->create_thread(boost::bind(&BitcoinMinerWallet, pwallet)); -} - -// Amount compression: -// * If the amount is 0, output 0 -// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9) -// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10) -// * call the result n -// * output 1 + 10*(9*n + d - 1) + e -// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9 -// (this is decodable, as d is in [1-9] and e is in [0-9]) - -uint64 CTxOutCompressor::CompressAmount(uint64 n) -{ - if (n == 0) - return 0; - int e = 0; - while (((n % 10) == 0) && e < 9) { - n /= 10; - e++; - } - if (e < 9) { - int d = (n % 10); - assert(d >= 1 && d <= 9); - n /= 10; - return 1 + (n*9 + d - 1)*10 + e; - } else { - return 1 + (n - 1)*10 + 9; - } -} - -uint64 CTxOutCompressor::DecompressAmount(uint64 x) -{ - // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9 - if (x == 0) - return 0; - x--; - // x = 10*(9*n + d - 1) + e - int e = x % 10; - x /= 10; - uint64 n = 0; - if (e < 9) { - // x = 9*n + d - 1 - int d = (x % 9) + 1; - x /= 9; - // x = n - n = x*10 + d; - } else { - n = x+1; - } - while (e) { - n *= 10; - e--; - } - return n; -} - - -class CMainCleanup -{ -public: - CMainCleanup() {} - ~CMainCleanup() { - // block headers - std::map::iterator it1 = mapBlockIndex.begin(); - for (; it1 != mapBlockIndex.end(); it1++) - delete (*it1).second; - mapBlockIndex.clear(); - - // orphan blocks - std::map::iterator it2 = mapOrphanBlocks.begin(); - for (; it2 != mapOrphanBlocks.end(); it2++) - delete (*it2).second; - mapOrphanBlocks.clear(); - - // orphan transactions - std::map::iterator it3 = mapOrphanTransactions.begin(); - for (; it3 != mapOrphanTransactions.end(); it3++) - delete (*it3).second; - mapOrphanTransactions.clear(); - } -} instance_of_cmaincleanup; diff --git a/src/main.h b/src/main.h index 3861e37e..d3cb0f94 100644 --- a/src/main.h +++ b/src/main.h @@ -9,21 +9,11 @@ #include "bignum.h" #include "sync.h" -#include "net.h" -#include "script.h" +#include "hash.h" #include -class CWallet; -class CBlock; class CBlockIndex; -class CKeyItem; -class CReserveKey; - -class CAddress; -class CInv; -class CNode; - struct CBlockIndexWorkComparator; /** The maximum allowed size for a serialized block, in bytes (network rule) */ @@ -58,1213 +48,8 @@ static const int COINBASE_MATURITY = 3000; static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC /** Maximum number of script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 16; -#ifdef USE_UPNP -static const int fHaveUPnP = true; -#else -static const int fHaveUPnP = false; -#endif - -static const uint256 hashGenesisBlockOfficial("0x963d17ba4dc753138078a2f56afb3af9674e2546822badff26837db9a0152106"); -static const uint256 hashGenesisBlockTestNet("0x221156cf301bc3585e72de34fe1efdb6fbd703bc27cfc468faa1cdd889d0efa0"); - -extern CScript COINBASE_FLAGS; - - - - - - -extern CCriticalSection cs_main; -extern std::map mapBlockIndex; -extern std::set setBlockIndexValid; -extern uint256 hashGenesisBlock; -extern CBlockIndex* pindexGenesisBlock; -extern int nBestHeight; -extern uint256 nBestChainWork; -extern uint256 nBestInvalidWork; -extern uint256 hashBestChain; -extern CBlockIndex* pindexBest; -extern unsigned int nTransactionsUpdated; -extern uint64 nLastBlockTx; -extern uint64 nLastBlockSize; -extern const std::string strMessageMagic; -extern double dPrimesPerSec; -extern double dChainsPerMinute; -extern double dChainsPerDay; -extern int64 nHPSTimerStart; -extern int64 nTimeBestReceived; -extern CCriticalSection cs_setpwalletRegistered; -extern std::set setpwalletRegistered; -extern std::map mapOrphanBlocks; -extern unsigned char pchMessageStart[4]; -extern bool fImporting; -extern bool fReindex; -extern bool fBenchmark; -extern int nScriptCheckThreads; -extern bool fTxIndex; -extern unsigned int nCoinCacheSize; - // Settings -extern int64 nTransactionFee; -// Minimum disk space required - used in CheckDiskSpace() -static const uint64 nMinDiskSpace = 52428800; - - -class CReserveKey; -class CCoinsDB; -class CBlockTreeDB; -struct CDiskBlockPos; -class CCoins; -class CTxUndo; -class CCoinsView; -class CCoinsViewCache; -class CScriptCheck; -class CValidationState; - -struct CBlockTemplate; - -/** Register a wallet to receive updates from core */ -void RegisterWallet(CWallet* pwalletIn); -/** Unregister a wallet from core */ -void UnregisterWallet(CWallet* pwalletIn); -/** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); -/** Process an incoming block */ -bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); -/** Check whether enough disk space is available for an incoming block */ -bool CheckDiskSpace(uint64 nAdditionalBytes = 0); -/** Open a block file (blk?????.dat) */ -FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); -/** Open an undo file (rev?????.dat) */ -FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); -/** Import blocks from an external file */ -bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); -/** Initialize a new block tree database + block data on disk */ -bool InitBlockIndex(); -/** Load the block tree and coins database from disk */ -bool LoadBlockIndex(); -/** Unload database information */ -void UnloadBlockIndex(); -/** Verify consistency of the block and coin databases */ -bool VerifyDB(); -/** Print the loaded block tree */ -void PrintBlockTree(); -/** Find a block by height in the currently-connected chain */ -CBlockIndex* FindBlockByHeight(int nHeight); -/** Process protocol messages received from a given node */ -bool ProcessMessages(CNode* pfrom); -/** Send queued protocol messages to be sent to a give node */ -bool SendMessages(CNode* pto, bool fSendTrickle); -/** Run an instance of the script checking thread */ -void ThreadScriptCheck(); -/** Run the miner threads */ -void GenerateBitcoins(bool fGenerate, CWallet* pwallet); -/** Generate a new block, without valid proof-of-work */ -CBlockTemplate* CreateNewBlock(CReserveKey& reservekey); -/** Modify the extranonce in a block */ -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); -/** Do mining precalculation */ -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); -/** Check mined block */ -bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); -/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ -bool CheckProofOfWork(uint256 hashBlockHeader, unsigned int nBits, const CBigNum& bnPrimeChainMultiplier, unsigned int& nChainType, unsigned int& nChainLength); -/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ -unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); -/** Get the number of active peers */ -int GetNumBlocksOfPeers(); -/** Check whether we are doing an initial block download (synchronizing from disk or network) */ -bool IsInitialBlockDownload(); -/** Format a string that describes several potential problems detected by the core */ -std::string GetWarnings(std::string strFor); -/** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); -/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ -bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); -/** Find the best known block, and make it the tip of the block chain */ -bool ConnectBestBlock(CValidationState &state); -/** Create a new block index entry for a given block hash */ -CBlockIndex * InsertBlockIndex(uint256 hash); -/** Verify a signature */ -bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); -/** Abort with a message */ -bool AbortNode(const std::string &msg); - - - - - - - - - - - -bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); - -struct CDiskBlockPos -{ - int nFile; - unsigned int nPos; - - IMPLEMENT_SERIALIZE( - READWRITE(VARINT(nFile)); - READWRITE(VARINT(nPos)); - ) - - CDiskBlockPos() { - SetNull(); - } - - CDiskBlockPos(int nFileIn, unsigned int nPosIn) { - nFile = nFileIn; - nPos = nPosIn; - } - - friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) { - return (a.nFile == b.nFile && a.nPos == b.nPos); - } - - friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { - return !(a == b); - } - - void SetNull() { nFile = -1; nPos = 0; } - bool IsNull() const { return (nFile == -1); } -}; - -struct CDiskTxPos : public CDiskBlockPos -{ - unsigned int nTxOffset; // after header - - IMPLEMENT_SERIALIZE( - READWRITE(*(CDiskBlockPos*)this); - READWRITE(VARINT(nTxOffset)); - ) - - CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) { - } - - CDiskTxPos() { - SetNull(); - } - - void SetNull() { - CDiskBlockPos::SetNull(); - nTxOffset = 0; - } -}; - - -/** An inpoint - a combination of a transaction and an index n into its vin */ -class CInPoint -{ -public: - CTransaction* ptx; - unsigned int n; - - CInPoint() { SetNull(); } - CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = (unsigned int) -1; } - bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } -}; - - - -/** An outpoint - a combination of a transaction hash and an index n into its vout */ -class COutPoint -{ -public: - uint256 hash; - unsigned int n; - - COutPoint() { SetNull(); } - COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } - IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) - void SetNull() { hash = 0; n = (unsigned int) -1; } - bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); } - - friend bool operator<(const COutPoint& a, const COutPoint& b) - { - return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); - } - - friend bool operator==(const COutPoint& a, const COutPoint& b) - { - return (a.hash == b.hash && a.n == b.n); - } - - friend bool operator!=(const COutPoint& a, const COutPoint& b) - { - return !(a == b); - } - - std::string ToString() const - { - return strprintf("COutPoint(%s, %u)", hash.ToString().c_str(), n); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -/** An input of a transaction. It contains the location of the previous - * transaction's output that it claims and a signature that matches the - * output's public key. - */ -class CTxIn -{ -public: - COutPoint prevout; - CScript scriptSig; - unsigned int nSequence; - - CTxIn() - { - nSequence = std::numeric_limits::max(); - } - - explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = prevoutIn; - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = COutPoint(hashPrevTx, nOut); - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); - ) - - bool IsFinal() const - { - return (nSequence == std::numeric_limits::max()); - } - - friend bool operator==(const CTxIn& a, const CTxIn& b) - { - return (a.prevout == b.prevout && - a.scriptSig == b.scriptSig && - a.nSequence == b.nSequence); - } - - friend bool operator!=(const CTxIn& a, const CTxIn& b) - { - return !(a == b); - } - - std::string ToString() const - { - std::string str; - str += "CTxIn("; - str += prevout.ToString(); - if (prevout.IsNull()) - str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); - else - str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); - if (nSequence != std::numeric_limits::max()) - str += strprintf(", nSequence=%u", nSequence); - str += ")"; - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -/** An output of a transaction. It contains the public key that the next input - * must be able to sign with to claim it. - */ -class CTxOut -{ -public: - int64 nValue; - CScript scriptPubKey; - - CTxOut() - { - SetNull(); - } - - CTxOut(int64 nValueIn, CScript scriptPubKeyIn) - { - nValue = nValueIn; - scriptPubKey = scriptPubKeyIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(nValue); - READWRITE(scriptPubKey); - ) - - void SetNull() - { - nValue = -1; - scriptPubKey.clear(); - } - - bool IsNull() const - { - return (nValue == -1); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - friend bool operator==(const CTxOut& a, const CTxOut& b) - { - return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); - } - - friend bool operator!=(const CTxOut& a, const CTxOut& b) - { - return !(a == b); - } - - bool IsDust() const; - - std::string ToString() const - { - if (scriptPubKey.size() < 6) - return "CTxOut(error)"; - return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - -enum GetMinFee_mode -{ - GMF_BLOCK, - GMF_RELAY, - GMF_SEND, -}; - -/** The basic transaction that is broadcasted on the network and contained in - * blocks. A transaction can contain multiple inputs and outputs. - */ -class CTransaction -{ -public: - static int64 nMinTxFee; - static int64 nMinRelayTxFee; - static const int CURRENT_VERSION=1; - int nVersion; - std::vector vin; - std::vector vout; - unsigned int nLockTime; - - CTransaction() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); - ) - - void SetNull() - { - nVersion = CTransaction::CURRENT_VERSION; - vin.clear(); - vout.clear(); - nLockTime = 0; - } - - bool IsNull() const - { - return (vin.empty() && vout.empty()); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const - { - // Time based nLockTime implemented in 0.1.6 - if (nLockTime == 0) - return true; - if (nBlockHeight == 0) - nBlockHeight = nBestHeight; - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) - return true; - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.IsFinal()) - return false; - return true; - } - - bool IsNewerThan(const CTransaction& old) const - { - if (vin.size() != old.vin.size()) - return false; - for (unsigned int i = 0; i < vin.size(); i++) - if (vin[i].prevout != old.vin[i].prevout) - return false; - - bool fNewer = false; - unsigned int nLowest = std::numeric_limits::max(); - for (unsigned int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != old.vin[i].nSequence) - { - if (vin[i].nSequence <= nLowest) - { - fNewer = false; - nLowest = vin[i].nSequence; - } - if (old.vin[i].nSequence < nLowest) - { - fNewer = true; - nLowest = old.vin[i].nSequence; - } - } - } - return fNewer; - } - - bool IsCoinBase() const - { - return (vin.size() == 1 && vin[0].prevout.IsNull()); - } - - /** Check for standard transaction types - @return True if all outputs (scriptPubKeys) use only standard transaction forms - */ - bool IsStandard() const; - - /** Check for standard transaction types - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return True if all inputs (scriptSigs) use only standard transaction forms - */ - bool AreInputsStandard(CCoinsViewCache& mapInputs) const; - - /** Count ECDSA signature operations the old-fashioned (pre-0.6) way - @return number of sigops this transaction's outputs will produce when spent - */ - unsigned int GetLegacySigOpCount() const; - - /** Count ECDSA signature operations in pay-to-script-hash inputs. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return maximum number of sigops required to validate this transaction's inputs - */ - unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; - - /** Amount of bitcoins spent by this transaction. - @return sum of all outputs (note: does not include fees) - */ - int64 GetValueOut() const - { - int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); - } - return nValueOut; - } - - /** Amount of bitcoins coming in to this transaction - Note that lightweight clients may not know anything besides the hash of previous transactions, - so may not be able to calculate this. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return Sum of value of all inputs (scriptSigs) - */ - int64 GetValueIn(CCoinsViewCache& mapInputs) const; - - static bool AllowFree(double dPriority) - { - // Large (in bytes) low-priority (new, small-coin) transactions - // need a fee. - return dPriority > COIN * 144 / 250; - } - - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK) const; - - friend bool operator==(const CTransaction& a, const CTransaction& b) - { - return (a.nVersion == b.nVersion && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); - } - - friend bool operator!=(const CTransaction& a, const CTransaction& b) - { - return !(a == b); - } - - - std::string ToString() const - { - std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", - GetHash().ToString().c_str(), - nVersion, - vin.size(), - vout.size(), - nLockTime); - for (unsigned int i = 0; i < vin.size(); i++) - str += " " + vin[i].ToString() + "\n"; - for (unsigned int i = 0; i < vout.size(); i++) - str += " " + vout[i].ToString() + "\n"; - return str; - } - - void print() const - { - printf("%s", ToString().c_str()); - } - - - // Check whether all prevouts of this transaction are present in the UTXO set represented by view - bool HaveInputs(CCoinsViewCache &view) const; - - // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) - // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it - // instead of being performed inline. - bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, - unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, - std::vector *pvChecks = NULL) const; - - // Apply the effects of this transaction on the UTXO set represented by view - void UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; - - // Context-independent validity checks - bool CheckTransaction(CValidationState &state) const; - - // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); - -protected: - static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); -}; - -/** wrapper for CTxOut that provides a more compact serialization */ -class CTxOutCompressor -{ -private: - CTxOut &txout; - -public: - static uint64 CompressAmount(uint64 nAmount); - static uint64 DecompressAmount(uint64 nAmount); - - CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } - - IMPLEMENT_SERIALIZE(({ - if (!fRead) { - uint64 nVal = CompressAmount(txout.nValue); - READWRITE(VARINT(nVal)); - } else { - uint64 nVal = 0; - READWRITE(VARINT(nVal)); - txout.nValue = DecompressAmount(nVal); - } - CScriptCompressor cscript(REF(txout.scriptPubKey)); - READWRITE(cscript); - });) -}; - -/** Undo information for a CTxIn - * - * Contains the prevout's CTxOut being spent, and if this was the - * last output of the affected transaction, its metadata as well - * (coinbase or not, height, transaction version) - */ -class CTxInUndo -{ -public: - CTxOut txout; // the txout data before being spent - bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase - unsigned int nHeight; // if the outpoint was the last unspent: its height - int nVersion; // if the outpoint was the last unspent: its version - - CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} - CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + - (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + - ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); - } - - template - void Serialize(Stream &s, int nType, int nVersion) const { - ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); - if (nHeight > 0) - ::Serialize(s, VARINT(this->nVersion), nType, nVersion); - ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); - } - - template - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nCode = 0; - ::Unserialize(s, VARINT(nCode), nType, nVersion); - nHeight = nCode / 2; - fCoinBase = nCode & 1; - if (nHeight > 0) - ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); - ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); - } -}; - -/** Undo information for a CTransaction */ -class CTxUndo -{ -public: - // undo information for all txins - std::vector vprevout; - - IMPLEMENT_SERIALIZE( - READWRITE(vprevout); - ) -}; - -/** Undo information for a CBlock */ -class CBlockUndo -{ -public: - std::vector vtxundo; // for all but the coinbase - - IMPLEMENT_SERIALIZE( - READWRITE(vtxundo); - ) - - bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) - { - // Open history file to append - CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CBlockUndo::WriteToDisk() : OpenUndoFile failed"); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); - fileout << FLATDATA(pchMessageStart) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout); - if (fileOutPos < 0) - return error("CBlockUndo::WriteToDisk() : ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << *this; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << *this; - fileout << hasher.GetHash(); - - // Flush stdio buffers and commit to disk before returning - fflush(fileout); - if (!IsInitialBlockDownload()) - FileCommit(fileout); - - return true; - } - - bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) - { - // Open history file to read - CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CBlockUndo::ReadFromDisk() : OpenBlockFile failed"); - - // Read block - uint256 hashChecksum; - try { - filein >> *this; - filein >> hashChecksum; - } - catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << *this; - if (hashChecksum != hasher.GetHash()) - return error("CBlockUndo::ReadFromDisk() : checksum mismatch"); - - return true; - } -}; - -/** pruned version of CTransaction: only retains metadata and unspent transaction outputs - * - * Serialized format: - * - VARINT(nVersion) - * - VARINT(nCode) - * - unspentness bitvector, for vout[2] and further; least significant byte first - * - the non-spent CTxOuts (via CTxOutCompressor) - * - VARINT(nHeight) - * - * The nCode value consists of: - * - bit 1: IsCoinBase() - * - bit 2: vout[0] is not spent - * - bit 4: vout[1] is not spent - * - The higher bits encode N, the number of non-zero bytes in the following bitvector. - * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at - * least one non-spent output). - * - * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e - * <><><--------------------------------------------><----> - * | \ | / - * version code vout[1] height - * - * - version = 1 - * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) - * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 - * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 - * * 8358: compact amount representation for 60000000000 (600 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 - * - height = 203998 - * - * - * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b - * <><><--><--------------------------------------------------><----------------------------------------------><----> - * / \ \ | | / - * version code unspentness vout[4] vout[16] height - * - * - version = 1 - * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, - * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) - * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent - * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee - * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 - * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 - * * bbd123: compact amount representation for 110397 (0.001 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 - * - height = 120891 - */ -class CCoins -{ -public: - // whether transaction is a coinbase - bool fCoinBase; - - // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped - std::vector vout; - - // at which height this transaction was included in the active block chain - int nHeight; - - // version of the CTransaction; accesses to this value should probably check for nHeight as well, - // as new tx version will probably only be introduced at certain heights - int nVersion; - - // construct a CCoins from a CTransaction, at a given height - CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } - - // empty constructor - CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } - - // remove spent outputs at the end of vout - void Cleanup() { - while (vout.size() > 0 && vout.back().IsNull()) - vout.pop_back(); - if (vout.empty()) - std::vector().swap(vout); - } - - void swap(CCoins &to) { - std::swap(to.fCoinBase, fCoinBase); - to.vout.swap(vout); - std::swap(to.nHeight, nHeight); - std::swap(to.nVersion, nVersion); - } - - // equality test - friend bool operator==(const CCoins &a, const CCoins &b) { - return a.fCoinBase == b.fCoinBase && - a.nHeight == b.nHeight && - a.nVersion == b.nVersion && - a.vout == b.vout; - } - friend bool operator!=(const CCoins &a, const CCoins &b) { - return !(a == b); - } - - // calculate number of bytes for the bitmask, and its number of non-zero bytes - // each bit in the bitmask represents the availability of one output, but the - // availabilities of the first two outputs are encoded separately - void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { - unsigned int nLastUsedByte = 0; - for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { - bool fZero = true; - for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { - if (!vout[2+b*8+i].IsNull()) { - fZero = false; - continue; - } - } - if (!fZero) { - nLastUsedByte = b + 1; - nNonzeroBytes++; - } - } - nBytes += nLastUsedByte; - } - - bool IsCoinBase() const { - return fCoinBase; - } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - unsigned int nSize = 0; - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); - // size of header code - nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); - // spentness bitmask - nSize += nMaskSize; - // txouts themself - for (unsigned int i = 0; i < vout.size(); i++) - if (!vout[i].IsNull()) - nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); - // height - nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); - return nSize; - } - - template - void Serialize(Stream &s, int nType, int nVersion) const { - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - ::Serialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Serialize(s, VARINT(nCode), nType, nVersion); - // spentness bitmask - for (unsigned int b = 0; b - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nCode = 0; - // version - ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Unserialize(s, VARINT(nCode), nType, nVersion); - fCoinBase = nCode & 1; - std::vector vAvail(2, false); - vAvail[0] = nCode & 2; - vAvail[1] = nCode & 4; - unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); - // spentness bitmask - while (nMaskCode > 0) { - unsigned char chAvail = 0; - ::Unserialize(s, chAvail, nType, nVersion); - for (unsigned int p = 0; p < 8; p++) { - bool f = (chAvail & (1 << p)) != 0; - vAvail.push_back(f); - } - if (chAvail != 0) - nMaskCode--; - } - // txouts themself - vout.assign(vAvail.size(), CTxOut()); - for (unsigned int i = 0; i < vAvail.size(); i++) { - if (vAvail[i]) - ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); - } - // coinbase height - ::Unserialize(s, VARINT(nHeight), nType, nVersion); - Cleanup(); - } - - // mark an outpoint spent, and construct undo information - bool Spend(const COutPoint &out, CTxInUndo &undo) { - if (out.n >= vout.size()) - return false; - if (vout[out.n].IsNull()) - return false; - undo = CTxInUndo(vout[out.n]); - vout[out.n].SetNull(); - Cleanup(); - if (vout.size() == 0) { - undo.nHeight = nHeight; - undo.fCoinBase = fCoinBase; - undo.nVersion = this->nVersion; - } - return true; - } - - // mark a vout spent - bool Spend(int nPos) { - CTxInUndo undo; - COutPoint out(0, nPos); - return Spend(out, undo); - } - - // check whether a particular output is still available - bool IsAvailable(unsigned int nPos) const { - return (nPos < vout.size() && !vout[nPos].IsNull()); - } - - // check whether the entire CCoins is spent - // note that only !IsPruned() CCoins can be serialized - bool IsPruned() const { - BOOST_FOREACH(const CTxOut &out, vout) - if (!out.IsNull()) - return false; - return true; - } -}; - -/** Closure representing one script verification - * Note that this stores references to the spending transaction */ -class CScriptCheck -{ -private: - CScript scriptPubKey; - const CTransaction *ptxTo; - unsigned int nIn; - unsigned int nFlags; - int nHashType; - -public: - CScriptCheck() {} - CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, int nHashTypeIn) : - scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), nHashType(nHashTypeIn) { } - - bool operator()() const; - - void swap(CScriptCheck &check) { - scriptPubKey.swap(check.scriptPubKey); - std::swap(ptxTo, check.ptxTo); - std::swap(nIn, check.nIn); - std::swap(nFlags, check.nFlags); - std::swap(nHashType, check.nHashType); - } -}; - -/** A transaction with a merkle branch linking it to the block chain. */ -class CMerkleTx : public CTransaction -{ -public: - uint256 hashBlock; - std::vector vMerkleBranch; - int nIndex; - - // memory only - mutable bool fMerkleVerified; - - - CMerkleTx() - { - Init(); - } - - CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) - { - Init(); - } - - void Init() - { - hashBlock = 0; - nIndex = -1; - fMerkleVerified = false; - } - - - 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(CBlockIndex* &pindexRet) const; - int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } - bool IsInMainChain() const { return GetDepthInMainChain() > 0; } - int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(bool fCheckInputs=true, bool fLimitFree=true); -}; - - - - - -/** Data structure that represents a partial merkle tree. - * - * It respresents a subset of the txid's of a known block, in a way that - * allows recovery of the list of txid's and the merkle root, in an - * authenticated way. - * - * The encoding works as follows: we traverse the tree in depth-first order, - * storing a bit for each traversed node, signifying whether the node is the - * parent of at least one matched leaf txid (or a matched txid itself). In - * case we are at the leaf level, or this bit is 0, its merkle node hash is - * stored, and its children are not explorer further. Otherwise, no hash is - * stored, but we recurse into both (or the only) child branch. During - * decoding, the same depth-first traversal is performed, consuming bits and - * hashes as they written during encoding. - * - * The serialization is fixed and provides a hard guarantee about the - * encoded size: - * - * SIZE <= 10 + ceil(32.25*N) - * - * Where N represents the number of leaf nodes of the partial tree. N itself - * is bounded by: - * - * N <= total_transactions - * N <= 1 + matched_transactions*tree_height - * - * The serialization format: - * - uint32 total_transactions (4 bytes) - * - varint number of hashes (1-3 bytes) - * - uint256[] hashes in depth-first order (<= 32*N bytes) - * - varint number of bytes of flag bits (1-3 bytes) - * - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits) - * The size constraints follow from this. - */ -class CPartialMerkleTree -{ -protected: - // the total number of transactions in the block - unsigned int nTransactions; - - // node-is-parent-of-matched-txid bits - std::vector vBits; - - // txids and internal hashes - std::vector vHash; - - // flag set when encountering invalid data - bool fBad; - - // helper function to efficiently calculate the number of nodes at given height in the merkle tree - unsigned int CalcTreeWidth(int height) { - return (nTransactions+(1 << height)-1) >> height; - } - - // calculate the hash of a node in the merkle tree (at leaf level: the txid's themself) - uint256 CalcHash(int height, unsigned int pos, const std::vector &vTxid); - - // recursive function that traverses tree nodes, storing the data as bits and hashes - void TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch); - - // recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild. - // it returns the hash of the respective node. - uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch); - -public: - - // serialization implementation - IMPLEMENT_SERIALIZE( - READWRITE(nTransactions); - READWRITE(vHash); - std::vector vBytes; - if (fRead) { - READWRITE(vBytes); - CPartialMerkleTree &us = *(const_cast(this)); - us.vBits.resize(vBytes.size() * 8); - for (unsigned int p = 0; p < us.vBits.size(); p++) - us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; - us.fBad = false; - } else { - vBytes.resize((vBits.size()+7)/8); - for (unsigned int p = 0; p < vBits.size(); p++) - vBytes[p / 8] |= vBits[p] << (p % 8); - READWRITE(vBytes); - } - ) - - // Construct a partial merkle tree from a list of transaction id's, and a mask that selects a subset of them - CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch); - - CPartialMerkleTree(); - - // extract the matching txid's represented by this partial merkle tree. - // returns the merkle root, or 0 in case of failure - uint256 ExtractMatches(std::vector &vMatch); -}; - - -/** Nodes collect new transactions into a block, hash them into a hash tree, - * and scan through nonce values to make the block's hash satisfy proof-of-work - * requirements. When they solve the proof-of-work, they broadcast the block - * to everyone and the block is added to the block chain. The first transaction - * in the block is a special one that creates a new coin owned by the creator - * of the block. - */ class CBlockHeader { public: @@ -1289,18 +74,6 @@ public: SetNull(); } - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); - READWRITE(bnPrimeChainMultiplier); - ) - void SetNull() { nVersion = CBlockHeader::CURRENT_VERSION; @@ -1323,14 +96,6 @@ public: return Hash(BEGIN(nVersion), END(nNonce)); } - // Primecoin: block hash includes prime certificate - uint256 GetHash() const - { - CDataStream ss(SER_GETHASH, 0); - ss << nVersion << hashPrevBlock << hashMerkleRoot << nTime << nBits << nNonce << bnPrimeChainMultiplier; - return Hash(ss.begin(), ss.end()); - } - int64 GetBlockTime() const { return (int64)nTime; @@ -1342,11 +107,6 @@ public: class CBlock : public CBlockHeader { public: - // network and disk - std::vector vtx; - - // memory only - mutable std::vector vMerkleTree; unsigned int nPrimeChainType; // primecoin: chain type (memory-only) unsigned int nPrimeChainLength; // primecoin: chain length (memory-only) @@ -1361,17 +121,9 @@ public: *((CBlockHeader*)this) = header; } - IMPLEMENT_SERIALIZE - ( - READWRITE(*(CBlockHeader*)this); - READWRITE(vtx); - ) - void SetNull() { CBlockHeader::SetNull(); - vtx.clear(); - vMerkleTree.clear(); nPrimeChainType = 0; nPrimeChainLength = 0; } @@ -1388,235 +140,20 @@ public: return block; } - uint256 BuildMerkleTree() const - { - vMerkleTree.clear(); - BOOST_FOREACH(const CTransaction& tx, vtx) - vMerkleTree.push_back(tx.GetHash()); - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - for (int i = 0; i < nSize; i += 2) - { - int i2 = std::min(i+1, nSize-1); - vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); - } - j += nSize; - } - return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); - } - - const uint256 &GetTxHash(unsigned int nIndex) const { - assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first - assert(nIndex < vtx.size()); - return vMerkleTree[nIndex]; - } - - std::vector GetMerkleBranch(int nIndex) const - { - if (vMerkleTree.empty()) - BuildMerkleTree(); - std::vector vMerkleBranch; - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - int i = std::min(nIndex^1, nSize-1); - vMerkleBranch.push_back(vMerkleTree[j+i]); - nIndex >>= 1; - j += nSize; - } - return vMerkleBranch; - } - - static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) - { - if (nIndex == -1) - return 0; - BOOST_FOREACH(const uint256& otherside, vMerkleBranch) - { - if (nIndex & 1) - hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); - else - hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); - nIndex >>= 1; - } - return hash; - } - - bool WriteToDisk(CDiskBlockPos &pos) - { - // Open history file to append - CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CBlock::WriteToDisk() : OpenBlockFile failed"); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); - fileout << FLATDATA(pchMessageStart) << nSize; - - // Write block - long fileOutPos = ftell(fileout); - if (fileOutPos < 0) - return error("CBlock::WriteToDisk() : ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << *this; - - // Flush stdio buffers and commit to disk before returning - fflush(fileout); - if (!IsInitialBlockDownload()) - FileCommit(fileout); - - return true; - } - - bool ReadFromDisk(const CDiskBlockPos &pos) - { - SetNull(); - - // Open history file to read - CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); - - // Read block - try { - filein >> *this; - } - catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - - // Primecoin: no proof-of-work check here unlike bitcoin - // Check the header - return true; - } - - - void print() const { - printf("CBlock(hash=%s, hashBlockHeader=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", - GetHash().ToString().c_str(), + printf("CBlock(hashBlockHeader=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u)\n", GetHeaderHash().ToString().c_str(), nVersion, hashPrevBlock.ToString().c_str(), hashMerkleRoot.ToString().c_str(), - nTime, nBits, nNonce, - vtx.size()); - for (unsigned int i = 0; i < vtx.size(); i++) - { - printf(" "); - vtx[i].print(); - } - printf(" vMerkleTree: "); - for (unsigned int i = 0; i < vMerkleTree.size(); i++) - printf("%s ", vMerkleTree[i].ToString().c_str()); + nTime, nBits, nNonce); printf("\n"); } - - - /** Undo the effects of this block (with given index) on the UTXO set represented by coins. - * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean - * will be true if no problems were found. Otherwise, the return value will be false in case - * of problems. Note that in any case, coins may be modified. */ - bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); - - // Apply the effects of this block (with given index) on the UTXO set represented by coins - bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); - - // Read a block from disk - bool ReadFromDisk(const CBlockIndex* pindex); - - // Add this block to the block index, and if necessary, switch the active block chain to this - bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos); - - // Context-independent validity checks - bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; - - // Store block on disk - // if dbp is provided, the file is known to already reside on disk - bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL); }; - - - - -class CBlockFileInfo -{ -public: - unsigned int nBlocks; // number of blocks stored in file - unsigned int nSize; // number of used bytes of block file - unsigned int nUndoSize; // number of used bytes in the undo file - unsigned int nHeightFirst; // lowest height of block in file - unsigned int nHeightLast; // highest height of block in file - uint64 nTimeFirst; // earliest time of block in file - uint64 nTimeLast; // latest time of block in file - - IMPLEMENT_SERIALIZE( - READWRITE(VARINT(nBlocks)); - READWRITE(VARINT(nSize)); - READWRITE(VARINT(nUndoSize)); - READWRITE(VARINT(nHeightFirst)); - READWRITE(VARINT(nHeightLast)); - READWRITE(VARINT(nTimeFirst)); - READWRITE(VARINT(nTimeLast)); - ) - - void SetNull() { - nBlocks = 0; - nSize = 0; - nUndoSize = 0; - nHeightFirst = 0; - nHeightLast = 0; - nTimeFirst = 0; - nTimeLast = 0; - } - - CBlockFileInfo() { - SetNull(); - } - - std::string ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); - } - - // update statistics (does not update nSize) - void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) { - if (nBlocks==0 || nHeightFirst > nHeightIn) - nHeightFirst = nHeightIn; - if (nBlocks==0 || nTimeFirst > nTimeIn) - nTimeFirst = nTimeIn; - nBlocks++; - if (nHeightIn > nHeightFirst) - nHeightLast = nHeightIn; - if (nTimeIn > nTimeLast) - nTimeLast = nTimeIn; - } -}; - -extern CCriticalSection cs_LastBlockFile; -extern CBlockFileInfo infoLastBlockFile; -extern int nLastBlockFile; - -enum BlockStatus { - BLOCK_VALID_UNKNOWN = 0, - BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future - BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint - BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root - BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 - BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok - BLOCK_VALID_MASK = 7, - - BLOCK_HAVE_DATA = 8, // full block available in blk*.dat - BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat - BLOCK_HAVE_MASK = 24, - - BLOCK_FAILED_VALID = 32, // stage after last reached validness failed - BLOCK_FAILED_CHILD = 64, // descends from failed block - BLOCK_FAILED_MASK = 96 -}; +class CBlockIndex; +extern CBlockIndex *pindexBest; /** The block chain is a tree shaped structure starting with the * genesis block at the root, with each block potentially having multiple @@ -1725,24 +262,6 @@ public: nNonce = block.nNonce; } - CDiskBlockPos GetBlockPos() const { - CDiskBlockPos ret; - if (nStatus & BLOCK_HAVE_DATA) { - ret.nFile = nFile; - ret.nPos = nDataPos; - } - return ret; - } - - CDiskBlockPos GetUndoPos() const { - CDiskBlockPos ret; - if (nStatus & BLOCK_HAVE_UNDO) { - ret.nFile = nFile; - ret.nPos = nUndoPos; - } - return ret; - } - CBlockHeader GetBlockHeader() const { CBlockHeader block; @@ -1842,447 +361,4 @@ struct CBlockIndexWorkComparator } }; - - -/** Used to marshal pointers into hashes for db storage. */ -class CDiskBlockIndex : public CBlockIndex -{ -public: - uint256 hashPrev; - uint256 hashBlock; // primecoin: persist block hash as well - - CDiskBlockIndex() { - hashPrev = 0; - hashBlock = 0; - } - - explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) { - hashPrev = (pprev ? pprev->GetBlockHash() : 0); - hashBlock = (pindex ? pindex->GetBlockHash() : 0); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(VARINT(nVersion)); - - READWRITE(VARINT(nPrimeChainType)); - READWRITE(VARINT(nPrimeChainLength)); - READWRITE(VARINT(nMoneySupply)); - READWRITE(VARINT(nHeight)); - READWRITE(VARINT(nStatus)); - READWRITE(VARINT(nTx)); - if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) - READWRITE(VARINT(nFile)); - if (nStatus & BLOCK_HAVE_DATA) - READWRITE(VARINT(nDataPos)); - if (nStatus & BLOCK_HAVE_UNDO) - READWRITE(VARINT(nUndoPos)); - - // block header - READWRITE(this->nVersion); - READWRITE(hashPrev); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); - READWRITE(hashBlock); - ) - - uint256 GetBlockHash() const - { - return hashBlock; - } - - - std::string ToString() const - { - std::string str = "CDiskBlockIndex("; - str += CBlockIndex::ToString(); - str += strprintf("\n hashBlock=%s, hashPrev=%s)", - GetBlockHash().ToString().c_str(), - hashPrev.ToString().c_str()); - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - -/** Capture information about block/transaction validation */ -class CValidationState { -private: - enum mode_state { - MODE_VALID, // everything ok - MODE_INVALID, // network rule violation (DoS value may be set) - MODE_ERROR, // run-time error - } mode; - int nDoS; -public: - CValidationState() : mode(MODE_VALID), nDoS(0) {} - bool DoS(int level, bool ret = false) { - if (mode == MODE_ERROR) - return ret; - nDoS += level; - mode = MODE_INVALID; - return ret; - } - bool Invalid(bool ret = false) { - return DoS(0, ret); - } - bool Error() { - mode = MODE_ERROR; - return false; - } - bool Abort(const std::string &msg) { - AbortNode(msg); - return Error(); - } - bool IsValid() { - return mode == MODE_VALID; - } - bool IsInvalid() { - return mode == MODE_INVALID; - } - bool IsError() { - return mode == MODE_ERROR; - } - bool IsInvalid(int &nDoSOut) { - if (IsInvalid()) { - nDoSOut = nDoS; - return true; - } - return false; - } -}; - - - - - - - -/** Describes a place in the block chain to another node such that if the - * other node doesn't have the same branch, it can find a recent common trunk. - * The further back it is, the further before the fork it may be. - */ -class CBlockLocator -{ -protected: - std::vector vHave; -public: - - CBlockLocator() - { - } - - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); - } - - explicit CBlockLocator(uint256 hashBlock) - { - std::map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - Set((*mi).second); - } - - CBlockLocator(const std::vector& vHaveIn) - { - vHave = vHaveIn; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) - - void SetNull() - { - vHave.clear(); - } - - bool IsNull() - { - return vHave.empty(); - } - - void Set(const CBlockIndex* pindex) - { - vHave.clear(); - int nStep = 1; - while (pindex) - { - vHave.push_back(pindex->GetBlockHash()); - - // Exponentially larger steps back - for (int i = 0; pindex && i < nStep; i++) - pindex = pindex->pprev; - if (vHave.size() > 10) - nStep *= 2; - } - vHave.push_back(hashGenesisBlock); - } - - int GetDistanceBack() - { - // Retrace how far back it was in the sender's branch - int nDistance = 0; - int nStep = 1; - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return nDistance; - } - nDistance += nStep; - if (nDistance > 10) - nStep *= 2; - } - return nDistance; - } - - CBlockIndex* GetBlockIndex() - { - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return pindex; - } - } - return pindexGenesisBlock; - } - - uint256 GetBlockHash() - { - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return hash; - } - } - return hashGenesisBlock; - } - - int GetHeight() - { - CBlockIndex* pindex = GetBlockIndex(); - if (!pindex) - return 0; - return pindex->nHeight; - } -}; - - - - - - - - -class CTxMemPool -{ -public: - mutable CCriticalSection cs; - std::map mapTx; - std::map mapNextTx; - - bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); - bool addUnchecked(const uint256& hash, CTransaction &tx); - bool remove(const CTransaction &tx, bool fRecursive = false); - bool removeConflicts(const CTransaction &tx); - void clear(); - void queryHashes(std::vector& vtxid); - void pruneSpent(const uint256& hash, CCoins &coins); - - unsigned long size() - { - LOCK(cs); - return mapTx.size(); - } - - bool exists(uint256 hash) - { - return (mapTx.count(hash) != 0); - } - - CTransaction& lookup(uint256 hash) - { - return mapTx[hash]; - } -}; - -extern CTxMemPool mempool; - -struct CCoinsStats -{ - int nHeight; - uint256 hashBlock; - uint64 nTransactions; - uint64 nTransactionOutputs; - uint64 nSerializedSize; - uint256 hashSerialized; - int64 nTotalAmount; - - CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} -}; - -/** Abstract view on the open txout dataset. */ -class CCoinsView -{ -public: - // Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(const uint256 &txid, CCoins &coins); - - // Modify the CCoins for a given txid - virtual bool SetCoins(const uint256 &txid, const CCoins &coins); - - // Just check whether we have data for a given txid. - // This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(const uint256 &txid); - - // Retrieve the block index whose state this CCoinsView currently represents - virtual CBlockIndex *GetBestBlock(); - - // Modify the currently active block index - virtual bool SetBestBlock(CBlockIndex *pindex); - - // Do a bulk modification (multiple SetCoins + one SetBestBlock) - virtual bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); - - // Calculate statistics about the unspent transaction output set - virtual bool GetStats(CCoinsStats &stats); - - // As we use CCoinsViews polymorphically, have a virtual destructor - virtual ~CCoinsView() {} -}; - -/** CCoinsView backed by another CCoinsView */ -class CCoinsViewBacked : public CCoinsView -{ -protected: - CCoinsView *base; - -public: - CCoinsViewBacked(CCoinsView &viewIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - void SetBackend(CCoinsView &viewIn); - bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); - bool GetStats(CCoinsStats &stats); -}; - -/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ -class CCoinsViewCache : public CCoinsViewBacked -{ -protected: - CBlockIndex *pindexTip; - std::map cacheCoins; - -public: - CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); - - // Standard CCoinsView methods - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); - - // Return a modifiable reference to a CCoins. Check HaveCoins first. - // Many methods explicitly require a CCoinsViewCache because of this method, to reduce - // copying. - CCoins &GetCoins(const uint256 &txid); - - // Push the modifications applied to this cache to its base. - // Failure to call this method before destruction will cause the changes to be forgotten. - bool Flush(); - - // Calculate the size of the cache (in number of transactions) - unsigned int GetCacheSize(); - -private: - std::map::iterator FetchCoins(const uint256 &txid); -}; - -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ -class CCoinsViewMemPool : public CCoinsViewBacked -{ -protected: - CTxMemPool &mempool; - -public: - CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool HaveCoins(const uint256 &txid); -}; - -/** Global variable that points to the active CCoinsView (protected by cs_main) */ -extern CCoinsViewCache *pcoinsTip; - -/** Global variable that points to the active block tree (protected by cs_main) */ -extern CBlockTreeDB *pblocktree; - -struct CBlockTemplate -{ - CBlock block; - std::vector vTxFees; - std::vector vTxSigOps; -}; - - - - - - -/** Used to relay blocks as header + vector - * to filtered nodes. - */ -class CMerkleBlock -{ -public: - // Public only for unit testing - CBlockHeader header; - CPartialMerkleTree txn; - -public: - // Public only for unit testing and relay testing - // (not relayed) - std::vector > vMatchedTxn; - - // Create from a CBlock, filtering transactions according to filter - // Note that this will call IsRelevantAndUpdate on the filter for each transaction, - // thus the filter will likely be modified. - CMerkleBlock(const CBlock& block, CBloomFilter& filter); - - IMPLEMENT_SERIALIZE - ( - READWRITE(header); - READWRITE(txn); - ) -}; - #endif diff --git a/src/main_poolminer.cpp b/src/main_poolminer.cpp index 17c33774..ff0a5406 100644 --- a/src/main_poolminer.cpp +++ b/src/main_poolminer.cpp @@ -11,8 +11,7 @@ #include #include "prime.h" -#include "serialize.h" -#include "bitcoinrpc.h" +//#include "bitcoinrpc.h" #include "json/json_spirit_value.h" #include #include @@ -24,15 +23,6 @@ #define MAX_THREADS 32 -// be compatible to original code (not actually used!) -#include "txdb.h" -#include "ui_interface.h" -CClientUIInterface uiInterface; -void StartShutdown() { - exit(0); -} -// - /********************************* * global variables, structs and extern functions *********************************/ diff --git a/src/makefile.unix b/src/makefile.unix index 92b3c058..8aa265ee 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -81,47 +81,13 @@ LIBS+= \ -l pthread -# Hardening -# Make some classes of vulnerabilities unexploitable in case one is discovered. -# - # This is a workaround for Ubuntu bug #691722, the default -fstack-protector causes - # -fstack-protector-all to be ignored unless -fno-stack-protector is used first. - # see: https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/691722 - HARDENING=-fno-stack-protector - - # Stack Canaries - # Put numbers at the beginning of each stack frame and check that they are the same. - # If a stack buffer if overflowed, it writes over the canary number and then on return - # when that number is checked, it won't be the same and the program will exit with - # a "Stack smashing detected" error instead of being exploited. - HARDENING+=-fstack-protector-all -Wstack-protector - - # Make some important things such as the global offset table read only as soon as - # the dynamic linker is finished building it. This will prevent overwriting of addresses - # which would later be jumped to. - LDHARDENING+=-Wl,-z,relro -Wl,-z,now - - # Build position independent code to take advantage of Address Space Layout Randomization - # offered by some kernels. - # see doc/build-unix.txt for more information. - ifdef PIE - HARDENING+=-fPIE - LDHARDENING+=-pie - endif - - # -D_FORTIFY_SOURCE=2 does some checking for potentially exploitable code patterns in - # the source such overflowing a statically defined buffer. - HARDENING+=-D_FORTIFY_SOURCE=2 -# - - #DEBUGFLAGS=-g3 -fno-inline -O0 DEBUGFLAGS= # CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only # adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. xCXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ - $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) -march=native + $(DEBUGFLAGS) $(DEFS) $(CXXFLAGS) -march=native # LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only # adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work. diff --git a/src/mruset.h b/src/mruset.h deleted file mode 100644 index a5273518..00000000 --- a/src/mruset.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_MRUSET_H -#define BITCOIN_MRUSET_H - -#include -#include - -/** STL-like set container that only keeps the most recent N elements. */ -template class mruset -{ -public: - typedef T key_type; - typedef T value_type; - typedef typename std::set::iterator iterator; - typedef typename std::set::const_iterator const_iterator; - typedef typename std::set::size_type size_type; - -protected: - std::set set; - std::deque queue; - size_type nMaxSize; - -public: - mruset(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } - iterator begin() const { return set.begin(); } - iterator end() const { return set.end(); } - size_type size() const { return set.size(); } - bool empty() const { return set.empty(); } - iterator find(const key_type& k) const { return set.find(k); } - size_type count(const key_type& k) const { return set.count(k); } - bool inline friend operator==(const mruset& a, const mruset& b) { return a.set == b.set; } - bool inline friend operator==(const mruset& a, const std::set& b) { return a.set == b; } - bool inline friend operator<(const mruset& a, const mruset& b) { return a.set < b.set; } - std::pair insert(const key_type& x) - { - std::pair ret = set.insert(x); - if (ret.second) - { - if (nMaxSize && queue.size() == nMaxSize) - { - set.erase(queue.front()); - queue.pop_front(); - } - queue.push_back(x); - } - return ret; - } - size_type max_size() const { return nMaxSize; } - size_type max_size(size_type s) - { - if (s) - while (queue.size() > s) - { - set.erase(queue.front()); - queue.pop_front(); - } - nMaxSize = s; - return nMaxSize; - } -}; - -#endif diff --git a/src/net.cpp b/src/net.cpp deleted file mode 100644 index 7e8a879c..00000000 --- a/src/net.cpp +++ /dev/null @@ -1,1893 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 The Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#include "db.h" -#include "net.h" -#include "init.h" -#include "addrman.h" -#include "ui_interface.h" -#include "script.h" - -#ifdef WIN32 -#include -#endif - -#ifdef USE_UPNP -#include -#include -#include -#include -#endif - -// Dump addresses to peers.dat every 15 minutes (900s) -#define DUMP_ADDRESSES_INTERVAL 900 - -using std::map; -using namespace std; -using namespace boost; - -static const int MAX_OUTBOUND_CONNECTIONS = 8; - -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); - - -struct LocalServiceInfo { - int nScore; - int nPort; -}; - -// -// Global state variables -// -bool fDiscover = true; -uint64 nLocalServices = NODE_NETWORK; -static CCriticalSection cs_mapLocalHost; -static map mapLocalHost; -static bool vfReachable[NET_MAX] = {}; -static bool vfLimited[NET_MAX] = {}; -static CNode* pnodeLocalHost = NULL; -static CNode* pnodeSync = NULL; -uint64 nLocalHostNonce = 0; -static std::vector vhListenSocket; -CAddrMan addrman; -int nMaxConnections = 125; - -vector vNodes; -CCriticalSection cs_vNodes; -map mapRelay; -deque > vRelayExpiration; -CCriticalSection cs_mapRelay; -limitedmap mapAlreadyAskedFor(MAX_INV_SZ); - -static deque vOneShots; -CCriticalSection cs_vOneShots; - -set setservAddNodeAddresses; -CCriticalSection cs_setservAddNodeAddresses; - -vector vAddedNodes; -CCriticalSection cs_vAddedNodes; - -static CSemaphore *semOutbound = NULL; - -void AddOneShot(string strDest) -{ - LOCK(cs_vOneShots); - vOneShots.push_back(strDest); -} - -unsigned short GetListenPort() -{ - return (unsigned short)(GetArg("-port", GetDefaultPort())); -} - -void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) -{ - // Filter out duplicate requests - if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) - return; - pindexLastGetBlocksBegin = pindexBegin; - hashLastGetBlocksEnd = hashEnd; - - PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); -} - -// find 'best' local address for a particular peer -bool GetLocal(CService& addr, const CNetAddr *paddrPeer) -{ - if (fNoListen) - return false; - - int nBestScore = -1; - int nBestReachability = -1; - { - LOCK(cs_mapLocalHost); - for (map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) - { - int nScore = (*it).second.nScore; - int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); - if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) - { - addr = CService((*it).first, (*it).second.nPort); - nBestReachability = nReachability; - nBestScore = nScore; - } - } - } - return nBestScore >= 0; -} - -// get best local address for a particular peer as a CAddress -CAddress GetLocalAddress(const CNetAddr *paddrPeer) -{ - CAddress ret(CService("0.0.0.0",0),0); - CService addr; - if (GetLocal(addr, paddrPeer)) - { - ret = CAddress(addr); - ret.nServices = nLocalServices; - ret.nTime = GetAdjustedTime(); - } - return ret; -} - -bool RecvLine(SOCKET hSocket, string& strLine) -{ - strLine = ""; - loop - { - char c; - int nBytes = recv(hSocket, &c, 1, 0); - if (nBytes > 0) - { - if (c == '\n') - continue; - if (c == '\r') - return true; - strLine += c; - if (strLine.size() >= 9000) - return true; - } - else if (nBytes <= 0) - { - boost::this_thread::interruption_point(); - if (nBytes < 0) - { - int nErr = WSAGetLastError(); - if (nErr == WSAEMSGSIZE) - continue; - if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) - { - MilliSleep(10); - continue; - } - } - if (!strLine.empty()) - return true; - if (nBytes == 0) - { - // socket closed - printf("socket closed\n"); - return false; - } - else - { - // socket error - int nErr = WSAGetLastError(); - printf("recv failed: %d\n", nErr); - return false; - } - } - } -} - -// used when scores of local addresses may have changed -// pushes better local address to peers -void static AdvertizeLocal() -{ - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { - if (pnode->fSuccessfullyConnected) - { - CAddress addrLocal = GetLocalAddress(&pnode->addr); - if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal) - { - pnode->PushAddress(addrLocal); - pnode->addrLocal = addrLocal; - } - } - } -} - -void SetReachable(enum Network net, bool fFlag) -{ - LOCK(cs_mapLocalHost); - vfReachable[net] = fFlag; - if (net == NET_IPV6 && fFlag) - vfReachable[NET_IPV4] = true; -} - -// learn a new local address -bool AddLocal(const CService& addr, int nScore) -{ - if (!addr.IsRoutable()) - return false; - - if (!fDiscover && nScore < LOCAL_MANUAL) - return false; - - if (IsLimited(addr)) - return false; - - printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); - - { - LOCK(cs_mapLocalHost); - bool fAlready = mapLocalHost.count(addr) > 0; - LocalServiceInfo &info = mapLocalHost[addr]; - if (!fAlready || nScore >= info.nScore) { - info.nScore = nScore + (fAlready ? 1 : 0); - info.nPort = addr.GetPort(); - } - SetReachable(addr.GetNetwork()); - } - - AdvertizeLocal(); - - return true; -} - -bool AddLocal(const CNetAddr &addr, int nScore) -{ - return AddLocal(CService(addr, GetListenPort()), nScore); -} - -/** Make a particular network entirely off-limits (no automatic connects to it) */ -void SetLimited(enum Network net, bool fLimited) -{ - if (net == NET_UNROUTABLE) - return; - LOCK(cs_mapLocalHost); - vfLimited[net] = fLimited; -} - -bool IsLimited(enum Network net) -{ - LOCK(cs_mapLocalHost); - return vfLimited[net]; -} - -bool IsLimited(const CNetAddr &addr) -{ - return IsLimited(addr.GetNetwork()); -} - -/** vote for a local address */ -bool SeenLocal(const CService& addr) -{ - { - LOCK(cs_mapLocalHost); - if (mapLocalHost.count(addr) == 0) - return false; - mapLocalHost[addr].nScore++; - } - - AdvertizeLocal(); - - return true; -} - -/** check whether a given address is potentially local */ -bool IsLocal(const CService& addr) -{ - LOCK(cs_mapLocalHost); - return mapLocalHost.count(addr) > 0; -} - -/** check whether a given address is in a network we can probably connect to */ -bool IsReachable(const CNetAddr& addr) -{ - LOCK(cs_mapLocalHost); - enum Network net = addr.GetNetwork(); - return vfReachable[net] && !vfLimited[net]; -} - -bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) -{ - SOCKET hSocket; - if (!ConnectSocket(addrConnect, hSocket)) - return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); - - send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); - - string strLine; - while (RecvLine(hSocket, strLine)) - { - if (strLine.empty()) // HTTP response is separated from headers by blank line - { - loop - { - if (!RecvLine(hSocket, strLine)) - { - closesocket(hSocket); - return false; - } - if (pszKeyword == NULL) - break; - if (strLine.find(pszKeyword) != string::npos) - { - strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); - break; - } - } - closesocket(hSocket); - if (strLine.find("<") != string::npos) - strLine = strLine.substr(0, strLine.find("<")); - strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); - while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) - strLine.resize(strLine.size()-1); - CService addr(strLine,0,true); - printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); - if (!addr.IsValid() || !addr.IsRoutable()) - return false; - ipRet.SetIP(addr); - return true; - } - } - closesocket(hSocket); - return error("GetMyExternalIP() : connection closed"); -} - -bool GetMyExternalIP(CNetAddr& ipRet) -{ - CService addrConnect; - const char* pszGet; - const char* pszKeyword; - - for (int nLookup = 0; nLookup <= 1; nLookup++) - for (int nHost = 1; nHost <= 2; nHost++) - { - // We should be phasing out our use of sites like these. If we need - // replacements, we should ask for volunteers to put this simple - // php file on their web server that prints the client IP: - // - if (nHost == 1) - { - addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org - - if (nLookup == 1) - { - CService addrIP("checkip.dyndns.org", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET / HTTP/1.1\r\n" - "Host: checkip.dyndns.org\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = "Address:"; - } - else if (nHost == 2) - { - addrConnect = CService("74.208.43.192", 80); // www.showmyip.com - - if (nLookup == 1) - { - CService addrIP("www.showmyip.com", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET /simple/ HTTP/1.1\r\n" - "Host: www.showmyip.com\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = NULL; // Returns just IP address - } - - if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) - return true; - } - - return false; -} - -void ThreadGetMyExternalIP(void* parg) -{ - // Make this thread recognisable as the external IP detection thread - RenameThread("primecoin-ext-ip"); - - CNetAddr addrLocalHost; - if (GetMyExternalIP(addrLocalHost)) - { - printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); - AddLocal(addrLocalHost, LOCAL_HTTP); - } -} - - - - - -void AddressCurrentlyConnected(const CService& addr) -{ - addrman.Connected(addr); -} - - - - - - - -CNode* FindNode(const CNetAddr& ip) -{ - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if ((CNetAddr)pnode->addr == ip) - return (pnode); - return NULL; -} - -CNode* FindNode(std::string addrName) -{ - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->addrName == addrName) - return (pnode); - return NULL; -} - -CNode* FindNode(const CService& addr) -{ - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if ((CService)pnode->addr == addr) - return (pnode); - return NULL; -} - -CNode* ConnectNode(CAddress addrConnect, const char *pszDest) -{ - if (pszDest == NULL) { - if (IsLocal(addrConnect)) - return NULL; - - // Look for an existing connection - CNode* pnode = FindNode((CService)addrConnect); - if (pnode) - { - pnode->AddRef(); - return pnode; - } - } - - - /// debug print - printf("trying connection %s lastseen=%.1fhrs\n", - pszDest ? pszDest : addrConnect.ToString().c_str(), - pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); - - // Connect - SOCKET hSocket; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket)) - { - addrman.Attempt(addrConnect); - - /// debug print - printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); - - // Set to non-blocking -#ifdef WIN32 - u_long nOne = 1; - if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) - printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError()); -#else - if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) - printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno); -#endif - - // Add node - CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); - pnode->AddRef(); - - { - LOCK(cs_vNodes); - vNodes.push_back(pnode); - } - - pnode->nTimeConnected = GetTime(); - return pnode; - } - else - { - return NULL; - } -} - -void CNode::CloseSocketDisconnect() -{ - fDisconnect = true; - if (hSocket != INVALID_SOCKET) - { - printf("disconnecting node %s\n", addrName.c_str()); - closesocket(hSocket); - hSocket = INVALID_SOCKET; - } - - // in case this fails, we'll empty the recv buffer when the CNode is deleted - TRY_LOCK(cs_vRecvMsg, lockRecv); - if (lockRecv) - vRecvMsg.clear(); - - // if this was the sync node, we'll need a new one - if (this == pnodeSync) - pnodeSync = NULL; -} - -void CNode::Cleanup() -{ -} - - -void CNode::PushVersion() -{ - /// when NTP implemented, change to just nTime = GetAdjustedTime() - int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); - CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); - CAddress addrMe = GetLocalAddress(&addr); - RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - printf("send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str()); - PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight); -} - - - - - -std::map CNode::setBanned; -CCriticalSection CNode::cs_setBanned; - -void CNode::ClearBanned() -{ - setBanned.clear(); -} - -bool CNode::IsBanned(CNetAddr ip) -{ - bool fResult = false; - { - LOCK(cs_setBanned); - std::map::iterator i = setBanned.find(ip); - if (i != setBanned.end()) - { - int64 t = (*i).second; - if (GetTime() < t) - fResult = true; - } - } - return fResult; -} - -bool CNode::Misbehaving(int howmuch) -{ - if (addr.IsLocal()) - { - printf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); - return false; - } - - nMisbehavior += howmuch; - if (nMisbehavior >= GetArg("-banscore", 100)) - { - int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban - printf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); - { - LOCK(cs_setBanned); - if (setBanned[addr] < banTime) - setBanned[addr] = banTime; - } - CloseSocketDisconnect(); - return true; - } else - printf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); - return false; -} - -#undef X -#define X(name) stats.name = name -void CNode::copyStats(CNodeStats &stats) -{ - X(nServices); - X(nLastSend); - X(nLastRecv); - X(nTimeConnected); - X(addrName); - X(nVersion); - X(strSubVer); - X(fInbound); - X(nStartingHeight); - X(nMisbehavior); - X(nSendBytes); - X(nRecvBytes); - stats.fSyncNode = (this == pnodeSync); -} -#undef X - -// requires LOCK(cs_vRecvMsg) -bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) -{ - while (nBytes > 0) { - - // get current incomplete message, or create a new one - if (vRecvMsg.empty() || - vRecvMsg.back().complete()) - vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion)); - - CNetMessage& msg = vRecvMsg.back(); - - // absorb network data - int handled; - if (!msg.in_data) - handled = msg.readHeader(pch, nBytes); - else - handled = msg.readData(pch, nBytes); - - if (handled < 0) - return false; - - pch += handled; - nBytes -= handled; - } - - return true; -} - -int CNetMessage::readHeader(const char *pch, unsigned int nBytes) -{ - // copy data to temporary parsing buffer - unsigned int nRemaining = 24 - nHdrPos; - unsigned int nCopy = std::min(nRemaining, nBytes); - - memcpy(&hdrbuf[nHdrPos], pch, nCopy); - nHdrPos += nCopy; - - // if header incomplete, exit - if (nHdrPos < 24) - return nCopy; - - // deserialize to CMessageHeader - try { - hdrbuf >> hdr; - } - catch (std::exception &e) { - return -1; - } - - // reject messages larger than MAX_SIZE - if (hdr.nMessageSize > MAX_SIZE) - return -1; - - // switch state to reading message data - in_data = true; - vRecv.resize(hdr.nMessageSize); - - return nCopy; -} - -int CNetMessage::readData(const char *pch, unsigned int nBytes) -{ - unsigned int nRemaining = hdr.nMessageSize - nDataPos; - unsigned int nCopy = std::min(nRemaining, nBytes); - - memcpy(&vRecv[nDataPos], pch, nCopy); - nDataPos += nCopy; - - return nCopy; -} - - - - - - - - - -// requires LOCK(cs_vSend) -void SocketSendData(CNode *pnode) -{ - std::deque::iterator it = pnode->vSendMsg.begin(); - - while (it != pnode->vSendMsg.end()) { - const CSerializeData &data = *it; - assert(data.size() > pnode->nSendOffset); - int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); - if (nBytes > 0) { - pnode->nLastSend = GetTime(); - pnode->nSendBytes += nBytes; - pnode->nSendOffset += nBytes; - if (pnode->nSendOffset == data.size()) { - pnode->nSendOffset = 0; - pnode->nSendSize -= data.size(); - it++; - } else { - // could not send full message; stop sending more - break; - } - } else { - if (nBytes < 0) { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - printf("socket send error %d\n", nErr); - pnode->CloseSocketDisconnect(); - } - } - // couldn't send anything at all - break; - } - } - - if (it == pnode->vSendMsg.end()) { - assert(pnode->nSendOffset == 0); - assert(pnode->nSendSize == 0); - } - pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); -} - -static list vNodesDisconnected; - -void ThreadSocketHandler() -{ - unsigned int nPrevNodeCount = 0; - loop - { - // - // Disconnect nodes - // - { - LOCK(cs_vNodes); - // Disconnect unused nodes - vector vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - { - if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty())) - { - // remove from vNodes - vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); - - // release outbound grant (if any) - pnode->grantOutbound.Release(); - - // close socket and cleanup - pnode->CloseSocketDisconnect(); - pnode->Cleanup(); - - // hold in disconnected pool until all refs are released - if (pnode->fNetworkNode || pnode->fInbound) - pnode->Release(); - vNodesDisconnected.push_back(pnode); - } - } - - // Delete disconnected nodes - list vNodesDisconnectedCopy = vNodesDisconnected; - BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) - { - // wait until threads are done using it - if (pnode->GetRefCount() <= 0) - { - bool fDelete = false; - { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { - TRY_LOCK(pnode->cs_inventory, lockInv); - if (lockInv) - fDelete = true; - } - } - } - if (fDelete) - { - vNodesDisconnected.remove(pnode); - delete pnode; - } - } - } - } - if (vNodes.size() != nPrevNodeCount) - { - nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(vNodes.size()); - } - - - // - // Find which sockets have data to receive - // - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 50000; // frequency to poll pnode->vSend - - fd_set fdsetRecv; - fd_set fdsetSend; - fd_set fdsetError; - FD_ZERO(&fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - SOCKET hSocketMax = 0; - bool have_fds = false; - - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { - FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); - have_fds = true; - } - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { - if (pnode->hSocket == INVALID_SOCKET) - continue; - FD_SET(pnode->hSocket, &fdsetError); - hSocketMax = max(hSocketMax, pnode->hSocket); - have_fds = true; - - // Implement the following logic: - // * If there is data to send, select() for sending data. As this only - // happens when optimistic write failed, we choose to first drain the - // write buffer in this case before receiving more. This avoids - // needlessly queueing received data, if the remote peer is not themselves - // receiving data. This means properly utilizing TCP flow control signalling. - // * Otherwise, if there is no (complete) message in the receive buffer, - // or there is space left in the buffer, select() for receiving data. - // * (if neither of the above applies, there is certainly one message - // in the receiver buffer ready to be processed). - // Together, that means that at least one of the following is always possible, - // so we don't deadlock: - // * We send some data. - // * We wait for data to be received (and disconnect after timeout). - // * We process a message in the buffer (message handler thread). - { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend && !pnode->vSendMsg.empty()) { - FD_SET(pnode->hSocket, &fdsetSend); - continue; - } - } - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv && ( - pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= ReceiveFloodSize())) - FD_SET(pnode->hSocket, &fdsetRecv); - } - } - } - - int nSelect = select(have_fds ? hSocketMax + 1 : 0, - &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - boost::this_thread::interruption_point(); - - if (nSelect == SOCKET_ERROR) - { - if (have_fds) - { - int nErr = WSAGetLastError(); - printf("socket select error %d\n", nErr); - for (unsigned int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); - } - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - MilliSleep(timeout.tv_usec/1000); - } - - - // - // Accept new connections - // - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) - if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) - { -#ifdef USE_IPV6 - struct sockaddr_storage sockaddr; -#else - struct sockaddr sockaddr; -#endif - socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); - CAddress addr; - int nInbound = 0; - - if (hSocket != INVALID_SOCKET) - if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) - printf("Warning: Unknown socket family\n"); - - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->fInbound) - nInbound++; - } - - if (hSocket == INVALID_SOCKET) - { - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK) - printf("socket error accept failed: %d\n", nErr); - } - else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) - { - { - LOCK(cs_setservAddNodeAddresses); - if (!setservAddNodeAddresses.count(addr)) - closesocket(hSocket); - } - } - else if (CNode::IsBanned(addr)) - { - printf("connection from %s dropped (banned)\n", addr.ToString().c_str()); - closesocket(hSocket); - } - else - { - printf("accepted connection %s\n", addr.ToString().c_str()); - CNode* pnode = new CNode(hSocket, addr, "", true); - pnode->AddRef(); - { - LOCK(cs_vNodes); - vNodes.push_back(pnode); - } - } - } - - - // - // Service each socket - // - vector vNodesCopy; - { - LOCK(cs_vNodes); - vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->AddRef(); - } - BOOST_FOREACH(CNode* pnode, vNodesCopy) - { - boost::this_thread::interruption_point(); - - // - // Receive - // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { - { - // typical socket buffer is 8K-64K - char pchBuf[0x10000]; - int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); - if (nBytes > 0) - { - if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) - pnode->CloseSocketDisconnect(); - pnode->nLastRecv = GetTime(); - pnode->nRecvBytes += nBytes; - } - else if (nBytes == 0) - { - // socket closed gracefully - if (!pnode->fDisconnect) - printf("socket closed\n"); - pnode->CloseSocketDisconnect(); - } - else if (nBytes < 0) - { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - if (!pnode->fDisconnect) - printf("socket recv error %d\n", nErr); - pnode->CloseSocketDisconnect(); - } - } - } - } - } - - // - // Send - // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetSend)) - { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - SocketSendData(pnode); - } - - // - // Inactivity checking - // - if (pnode->vSendMsg.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 > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) - { - printf("socket not sending\n"); - pnode->fDisconnect = true; - } - else if (GetTime() - pnode->nLastRecv > 90*60) - { - printf("socket inactivity timeout\n"); - pnode->fDisconnect = true; - } - } - } - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->Release(); - } - - MilliSleep(10); - } -} - - - - - - - - - -#ifdef USE_UPNP -void ThreadMapPort() -{ - std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = 0; - const char * minissdpdpath = 0; - struct UPNPDev * devlist = 0; - char lanaddr[64]; - -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); -#else - /* miniupnpc 1.6 */ - int error = 0; - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); -#endif - - struct UPNPUrls urls; - struct IGDdatas data; - int r; - - r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - if (r == 1) - { - if (fDiscover) { - char externalIPAddress[40]; - r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); - if(r != UPNPCOMMAND_SUCCESS) - printf("UPnP: GetExternalIPAddress() returned %d\n", r); - else - { - if(externalIPAddress[0]) - { - printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); - AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); - } - else - printf("UPnP: GetExternalIPAddress failed.\n"); - } - } - - string strDesc = "Primecoin " + FormatFullVersion(); - - try { - loop { -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); -#else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); -#endif - - if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); - else - printf("UPnP Port Mapping successful.\n");; - - MilliSleep(20*60*1000); // Refresh every 20 minutes - } - } - catch (boost::thread_interrupted) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - printf("UPNP_DeletePortMapping() returned : %d\n", r); - freeUPNPDevlist(devlist); devlist = 0; - FreeUPNPUrls(&urls); - throw; - } - } else { - printf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = 0; - if (r != 0) - FreeUPNPUrls(&urls); - } -} - -void MapPort(bool fUseUPnP) -{ - static boost::thread* upnp_thread = NULL; - - if (fUseUPnP) - { - if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - delete upnp_thread; - } - upnp_thread = new boost::thread(boost::bind(&TraceThread >, "upnp", &ThreadMapPort)); - } - else if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - delete upnp_thread; - upnp_thread = NULL; - } -} - -#else -void MapPort(bool) -{ - // Intentionally left blank. -} -#endif - - - - - - - - - -// DNS seeds -// Each pair gives a source name and a seed name. -// The first name is used as information source for addrman. -// The second name should resolve to a list of seed addresses. -static const char *strMainNetDNSSeed[][2] = { - {"primecoin.net", "seed.ppcoin.net"}, - {"xpm.altcointech.net", "dnsseed.xpm.altcointech.net"}, - {"xpm2.altcointech.net", "dnsseed.xpm2.altcointech.net"}, - {NULL, NULL} -}; - -static const char *strTestNetDNSSeed[][2] = { - {"primecoin.net", "tnseed.ppcoin.net"}, - {NULL, NULL} -}; - -void ThreadDNSAddressSeed() -{ - static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; - - int found = 0; - - printf("Loading addresses from DNS seeds (could take a while)\n"); - - for (unsigned int seed_idx = 0; strDNSSeed[seed_idx][0] != NULL; seed_idx++) { - if (HaveNameProxy()) { - AddOneShot(strDNSSeed[seed_idx][1]); - } else { - vector vaddr; - vector vAdd; - if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) - { - BOOST_FOREACH(CNetAddr& ip, vaddr) - { - int nOneDay = 24*3600; - CAddress addr = CAddress(CService(ip, GetDefaultPort())); - addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old - vAdd.push_back(addr); - found++; - } - } - addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); - } - } - - printf("%d addresses found from DNS seeds\n", found); -} - - - - - - - - - - - - -unsigned int pnSeedMainNet[] = -{ - 0x201621b2, 0x3a38be58, 0xde3cc718, 0x732dfb54, 0xf3c645d3, 0x48926257, - 0x746f1f4e, 0xaed7175e, -}; - -unsigned int pnSeedTestNet[] = -{ - 0x0a1621b2, -}; - -void DumpAddresses() -{ - int64 nStart = GetTimeMillis(); - - CAddrDB adb; - adb.Write(addrman); - - printf("Flushed %d addresses to peers.dat %"PRI64d"ms\n", - addrman.size(), GetTimeMillis() - nStart); -} - -void static ProcessOneShot() -{ - string strDest; - { - LOCK(cs_vOneShots); - if (vOneShots.empty()) - return; - strDest = vOneShots.front(); - vOneShots.pop_front(); - } - CAddress addr; - CSemaphoreGrant grant(*semOutbound, true); - if (grant) { - if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true)) - AddOneShot(strDest); - } -} - -void ThreadOpenConnections() -{ - // Connect to specific addresses - if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) - { - for (int64 nLoop = 0;; nLoop++) - { - ProcessOneShot(); - BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) - { - CAddress addr; - OpenNetworkConnection(addr, NULL, strAddr.c_str()); - for (int i = 0; i < 10 && i < nLoop; i++) - { - MilliSleep(500); - } - } - MilliSleep(500); - } - } - - // Initiate network connections - int64 nStart = GetTime(); - loop - { - ProcessOneShot(); - - MilliSleep(500); - - CSemaphoreGrant grant(*semOutbound); - boost::this_thread::interruption_point(); - - // Add seed nodes if IRC isn't working - if (addrman.size()==0 && (GetTime() - nStart > 60)) - { - static const unsigned int *pnSeed = fTestNet? pnSeedTestNet : pnSeedMainNet; - std::vector vAdd; - for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++) - { - // It'll only connect to one or two seed nodes because once it connects, - // it'll get a pile of addresses with newer timestamps. - // Seed nodes are given a random 'last seen time' of between one and two - // weeks ago. - const int64 nOneWeek = 7*24*60*60; - struct in_addr ip; - memcpy(&ip, &pnSeed[i], sizeof(ip)); - CAddress addr(CService(ip, GetDefaultPort())); - addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek; - vAdd.push_back(addr); - } - addrman.Add(vAdd, CNetAddr("127.0.0.1")); - } - - // - // Choose an address to connect to based on most recently seen - // - CAddress addrConnect; - - // Only connect out to one peer per network group (/16 for IPv4). - // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. - int nOutbound = 0; - set > setConnected; - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { - if (!pnode->fInbound) { - setConnected.insert(pnode->addr.GetGroup()); - nOutbound++; - } - } - } - - int64 nANow = GetAdjustedTime(); - - int nTries = 0; - loop - { - // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections) - CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); - - // if we selected an invalid address, restart - if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) - break; - - // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, - // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates - // already-connected network ranges, ...) before trying new addrman addresses. - nTries++; - if (nTries > 100) - break; - - if (IsLimited(addr)) - continue; - - // only consider very recently tried nodes after 30 failed attempts - if (nANow - addr.nLastTry < 600 && nTries < 30) - continue; - - // do not allow non-default ports, unless after 50 invalid addresses selected already - if (addr.GetPort() != GetDefaultPort() && nTries < 50) - continue; - - addrConnect = addr; - break; - } - - if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect, &grant); - } -} - -void ThreadOpenAddedConnections() -{ - { - LOCK(cs_vAddedNodes); - vAddedNodes = mapMultiArgs["-addnode"]; - } - - if (HaveNameProxy()) { - while(true) { - list lAddresses(0); - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) - lAddresses.push_back(strAddNode); - } - BOOST_FOREACH(string& strAddNode, lAddresses) { - CAddress addr; - CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(addr, &grant, strAddNode.c_str()); - MilliSleep(500); - } - MilliSleep(120000); // Retry every 2 minutes - } - } - - for (unsigned int i = 0; true; i++) - { - list lAddresses(0); - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) - lAddresses.push_back(strAddNode); - } - - list > lservAddressesToAdd(0); - BOOST_FOREACH(string& strAddNode, lAddresses) - { - vector vservNode(0); - if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) - { - lservAddressesToAdd.push_back(vservNode); - { - LOCK(cs_setservAddNodeAddresses); - BOOST_FOREACH(CService& serv, vservNode) - setservAddNodeAddresses.insert(serv); - } - } - } - // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry - // (keeping in mind that addnode entries can have many IPs if fNameLookup) - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - for (list >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) - BOOST_FOREACH(CService& addrNode, *(it)) - if (pnode->addr == addrNode) - { - it = lservAddressesToAdd.erase(it); - it--; - break; - } - } - BOOST_FOREACH(vector& vserv, lservAddressesToAdd) - { - CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); - MilliSleep(500); - } - MilliSleep(120000); // Retry every 2 minutes - } -} - -// if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot) -{ - // - // Initiate outbound network connection - // - boost::this_thread::interruption_point(); - if (!strDest) - if (IsLocal(addrConnect) || - FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || - FindNode(addrConnect.ToStringIPPort().c_str())) - return false; - if (strDest && FindNode(strDest)) - return false; - - CNode* pnode = ConnectNode(addrConnect, strDest); - boost::this_thread::interruption_point(); - - if (!pnode) - return false; - if (grantOutbound) - grantOutbound->MoveTo(pnode->grantOutbound); - pnode->fNetworkNode = true; - if (fOneShot) - pnode->fOneShot = true; - - return true; -} - - -// for now, use a very simple selection metric: the node from which we received -// most recently -double static NodeSyncScore(const CNode *pnode) { - return -pnode->nLastRecv; -} - -void static StartSync(const vector &vNodes) { - CNode *pnodeNewSync = NULL; - double dBestScore = 0; - - // fImporting and fReindex are accessed out of cs_main here, but only - // as an optimization - they are checked again in SendMessages. - if (fImporting || fReindex) - return; - - // Iterate over all nodes - BOOST_FOREACH(CNode* pnode, vNodes) { - // check preconditions for allowing a sync - if (!pnode->fClient && !pnode->fOneShot && - !pnode->fDisconnect && pnode->fSuccessfullyConnected && - (pnode->nStartingHeight > (nBestHeight - 144)) && - (pnode->nVersion < NOBLKS_VERSION_START || pnode->nVersion >= NOBLKS_VERSION_END)) { - // if ok, compare node's score with the best so far - double dScore = NodeSyncScore(pnode); - if (pnodeNewSync == NULL || dScore > dBestScore) { - pnodeNewSync = pnode; - dBestScore = dScore; - } - } - } - // if a new sync candidate was found, start sync! - if (pnodeNewSync) { - pnodeNewSync->fStartSync = true; - pnodeSync = pnodeNewSync; - } -} - -void ThreadMessageHandler() -{ - SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); - while (true) - { - bool fHaveSyncNode = false; - - vector vNodesCopy; - { - LOCK(cs_vNodes); - vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) { - pnode->AddRef(); - if (pnode == pnodeSync) - fHaveSyncNode = true; - } - } - - if (!fHaveSyncNode) - StartSync(vNodesCopy); - - // Poll the connected nodes for messages - CNode* pnodeTrickle = NULL; - if (!vNodesCopy.empty()) - pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - { - if (pnode->fDisconnect) - continue; - - // Receive messages - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - if (!ProcessMessages(pnode)) - pnode->CloseSocketDisconnect(); - } - boost::this_thread::interruption_point(); - - // Send messages - { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - SendMessages(pnode, pnode == pnodeTrickle); - } - boost::this_thread::interruption_point(); - } - - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->Release(); - } - - MilliSleep(100); - } -} - - - - - - -bool BindListenPort(const CService &addrBind, string& strError) -{ - strError = ""; - int nOne = 1; - - // Create socket for listening for incoming connections -#ifdef USE_IPV6 - struct sockaddr_storage sockaddr; -#else - struct sockaddr sockaddr; -#endif - socklen_t len = sizeof(sockaddr); - if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) - { - strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString().c_str()); - printf("%s\n", strError.c_str()); - return false; - } - - SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); - if (hListenSocket == INVALID_SOCKET) - { - strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - -#ifdef SO_NOSIGPIPE - // Different way of disabling SIGPIPE on BSD - setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); -#endif - -#ifndef WIN32 - // Allow binding if the port is still in TIME_WAIT state after - // the program was closed and restarted. Not an issue on windows. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); -#endif - - -#ifdef WIN32 - // Set to non-blocking, incoming connections will also inherit this - if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) -#else - if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) -#endif - { - strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - -#ifdef USE_IPV6 - // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option - // and enable it by default or not. Try to enable it, if possible. - if (addrBind.IsIPv6()) { -#ifdef WIN32 - int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; - int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; - // this call is allowed to fail - setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int)); -#else -#ifdef IPV6_V6ONLY - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); -#endif -#endif - } -#endif - - if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - if (nErr == WSAEADDRINUSE) - strError = strprintf(_("Unable to bind to %s on this computer. Primecoin is probably already running."), addrBind.ToString().c_str()); - else - strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr)); - printf("%s\n", strError.c_str()); - return false; - } - printf("Bound to %s\n", addrBind.ToString().c_str()); - - // Listen for incoming connections - if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) - { - strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - vhListenSocket.push_back(hListenSocket); - - if (addrBind.IsRoutable() && fDiscover) - AddLocal(addrBind, LOCAL_BIND); - - return true; -} - -void static Discover() -{ - if (!fDiscover) - return; - -#ifdef WIN32 - // Get local host IP - char pszHostName[1000] = ""; - if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) - { - vector vaddr; - if (LookupHost(pszHostName, vaddr)) - { - BOOST_FOREACH (const CNetAddr &addr, vaddr) - { - AddLocal(addr, LOCAL_IF); - } - } - } -#else - // Get local host ip - struct ifaddrs* myaddrs; - if (getifaddrs(&myaddrs) == 0) - { - for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr == NULL) continue; - if ((ifa->ifa_flags & IFF_UP) == 0) continue; - if (strcmp(ifa->ifa_name, "lo") == 0) continue; - if (strcmp(ifa->ifa_name, "lo0") == 0) continue; - if (ifa->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); - CNetAddr addr(s4->sin_addr); - if (AddLocal(addr, LOCAL_IF)) - printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); - } -#ifdef USE_IPV6 - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); - CNetAddr addr(s6->sin6_addr); - if (AddLocal(addr, LOCAL_IF)) - printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); - } -#endif - } - freeifaddrs(myaddrs); - } -#endif - - // Don't use external IPv4 discovery, when -onlynet="IPv6" - if (!IsLimited(NET_IPV4)) - NewThread(ThreadGetMyExternalIP, NULL); -} - -void StartNode(boost::thread_group& threadGroup) -{ - if (semOutbound == NULL) { - // initialize semaphore - int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); - semOutbound = new CSemaphore(nMaxOutbound); - } - - if (pnodeLocalHost == NULL) - pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); - - Discover(); - - // - // Start threads - // - - if (!GetBoolArg("-dnsseed", true)) - printf("DNS seeding disabled\n"); - else - threadGroup.create_thread(boost::bind(&TraceThread >, "dnsseed", &ThreadDNSAddressSeed)); - -#ifdef USE_UPNP - // Map ports with UPnP - MapPort(GetBoolArg("-upnp", USE_UPNP)); -#endif - - // Send and receive from sockets, accept connections - threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); - - // Initiate outbound connections from -addnode - threadGroup.create_thread(boost::bind(&TraceThread, "addcon", &ThreadOpenAddedConnections)); - - // Initiate outbound connections - threadGroup.create_thread(boost::bind(&TraceThread, "opencon", &ThreadOpenConnections)); - - // Process messages - threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); - - // Dump network addresses - threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000)); -} - -bool StopNode() -{ - printf("StopNode()\n"); - GenerateBitcoins(false, NULL); - MapPort(false); - nTransactionsUpdated++; - if (semOutbound) - for (int i=0; ipost(); - MilliSleep(50); - DumpAddresses(); - - return true; -} - -class CNetCleanup -{ -public: - CNetCleanup() - { - } - ~CNetCleanup() - { - // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - closesocket(pnode->hSocket); - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) - if (hListenSocket != INVALID_SOCKET) - if (closesocket(hListenSocket) == SOCKET_ERROR) - printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); - - // clean up some globals (to help leak detection) - BOOST_FOREACH(CNode *pnode, vNodes) - delete pnode; - BOOST_FOREACH(CNode *pnode, vNodesDisconnected) - delete pnode; - vNodes.clear(); - vNodesDisconnected.clear(); - delete semOutbound; - semOutbound = NULL; - delete pnodeLocalHost; - pnodeLocalHost = NULL; - -#ifdef WIN32 - // Shutdown Windows Sockets - WSACleanup(); -#endif - } -} -instance_of_cnetcleanup; - - - - - - - -void RelayTransaction(const CTransaction& tx, const uint256& hash) -{ - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(10000); - ss << tx; - RelayTransaction(tx, hash, ss); -} - -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss) -{ - CInv inv(MSG_TX, hash); - { - LOCK(cs_mapRelay); - // Expire old relay messages - while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) - { - mapRelay.erase(vRelayExpiration.front().second); - vRelayExpiration.pop_front(); - } - - // Save original serialized message so newer versions are preserved - mapRelay.insert(std::make_pair(inv, ss)); - vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); - } - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { - if(!pnode->fRelayTxes) - continue; - LOCK(pnode->cs_filter); - if (pnode->pfilter) - { - if (pnode->pfilter->IsRelevantAndUpdate(tx, hash)) - pnode->PushInventory(inv); - } else - pnode->PushInventory(inv); - } -} diff --git a/src/net.h b/src/net.h deleted file mode 100644 index 4c1ccf81..00000000 --- a/src/net.h +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_NET_H -#define BITCOIN_NET_H - -#include -#include -#include -#include - -#ifndef WIN32 -#include -#endif - -#include "mruset.h" -#include "limitedmap.h" -#include "netbase.h" -#include "protocol.h" -#include "addrman.h" -#include "hash.h" -#include "bloom.h" - -class CNode; -class CBlockIndex; -extern int nBestHeight; - - - -inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } -inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } - -void AddOneShot(std::string strDest); -bool RecvLine(SOCKET hSocket, std::string& strLine); -bool GetMyExternalIP(CNetAddr& ipRet); -void AddressCurrentlyConnected(const CService& addr); -CNode* FindNode(const CNetAddr& ip); -CNode* FindNode(const CService& ip); -CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL); -void MapPort(bool fUseUPnP); -unsigned short GetListenPort(); -bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); -void StartNode(boost::thread_group& threadGroup); -bool StopNode(); -void SocketSendData(CNode *pnode); - -enum -{ - LOCAL_NONE, // unknown - LOCAL_IF, // address a local interface listens on - LOCAL_BIND, // address explicit bound to - LOCAL_UPNP, // address reported by UPnP - LOCAL_HTTP, // address reported by whatismyip.com and similar - LOCAL_MANUAL, // address explicitly specified (-externalip=) - - LOCAL_MAX -}; - -void SetLimited(enum Network net, bool fLimited = true); -bool IsLimited(enum Network net); -bool IsLimited(const CNetAddr& addr); -bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); -bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); -bool SeenLocal(const CService& addr); -bool IsLocal(const CService& addr); -bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); -bool IsReachable(const CNetAddr &addr); -void SetReachable(enum Network net, bool fFlag = true); -CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); - - -extern bool fDiscover; -extern uint64 nLocalServices; -extern uint64 nLocalHostNonce; -extern CAddrMan addrman; -extern int nMaxConnections; - -extern std::vector vNodes; -extern CCriticalSection cs_vNodes; -extern std::map mapRelay; -extern std::deque > vRelayExpiration; -extern CCriticalSection cs_mapRelay; -extern limitedmap mapAlreadyAskedFor; - -extern std::vector vAddedNodes; -extern CCriticalSection cs_vAddedNodes; - - - - -class CNodeStats -{ -public: - uint64 nServices; - int64 nLastSend; - int64 nLastRecv; - int64 nTimeConnected; - std::string addrName; - int nVersion; - std::string strSubVer; - bool fInbound; - int nStartingHeight; - int nMisbehavior; - uint64 nSendBytes; - uint64 nRecvBytes; - bool fSyncNode; -}; - - - - -class CNetMessage { -public: - bool in_data; // parsing header (false) or data (true) - - CDataStream hdrbuf; // partially received header - CMessageHeader hdr; // complete header - unsigned int nHdrPos; - - CDataStream vRecv; // received message data - unsigned int nDataPos; - - CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) { - hdrbuf.resize(24); - in_data = false; - nHdrPos = 0; - nDataPos = 0; - } - - bool complete() const - { - if (!in_data) - return false; - return (hdr.nMessageSize == nDataPos); - } - - void SetVersion(int nVersionIn) - { - hdrbuf.SetVersion(nVersionIn); - vRecv.SetVersion(nVersionIn); - } - - int readHeader(const char *pch, unsigned int nBytes); - int readData(const char *pch, unsigned int nBytes); -}; - - - - - -/** Information about a peer */ -class CNode -{ -public: - // socket - uint64 nServices; - SOCKET hSocket; - CDataStream ssSend; - size_t nSendSize; // total size of all vSendMsg entries - size_t nSendOffset; // offset inside the first vSendMsg already sent - uint64 nSendBytes; - std::deque vSendMsg; - CCriticalSection cs_vSend; - - std::deque vRecvGetData; - std::deque vRecvMsg; - CCriticalSection cs_vRecvMsg; - uint64 nRecvBytes; - int nRecvVersion; - - int64 nLastSend; - int64 nLastRecv; - int64 nLastSendEmpty; - int64 nTimeConnected; - CAddress addr; - std::string addrName; - CService addrLocal; - int nVersion; - std::string strSubVer; - bool fOneShot; - bool fClient; - bool fInbound; - bool fNetworkNode; - bool fSuccessfullyConnected; - bool fDisconnect; - // We use fRelayTxes for two purposes - - // a) it allows us to not relay tx invs before receiving the peer's version message - // b) the peer may tell us in their version message that we should not relay tx invs - // until they have initialized their bloom filter. - bool fRelayTxes; - CSemaphoreGrant grantOutbound; - CCriticalSection cs_filter; - CBloomFilter* pfilter; - int nRefCount; -protected: - - // Denial-of-service detection/prevention - // Key is IP address, value is banned-until-time - static std::map setBanned; - static CCriticalSection cs_setBanned; - int nMisbehavior; - -public: - uint256 hashContinue; - CBlockIndex* pindexLastGetBlocksBegin; - uint256 hashLastGetBlocksEnd; - int nStartingHeight; - bool fStartSync; - - // flood relay - std::vector vAddrToSend; - std::set setAddrKnown; - bool fGetAddr; - std::set setKnown; - uint256 hashCheckpointKnown; // ppcoin: known sent sync-checkpoint - - // inventory based relay - mruset setInventoryKnown; - std::vector vInventoryToSend; - CCriticalSection cs_inventory; - std::multimap mapAskFor; - - CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION) - { - nServices = 0; - hSocket = hSocketIn; - nRecvVersion = MIN_PROTO_VERSION; - nLastSend = 0; - nLastRecv = 0; - nSendBytes = 0; - nRecvBytes = 0; - nLastSendEmpty = GetTime(); - nTimeConnected = GetTime(); - addr = addrIn; - addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; - nVersion = 0; - strSubVer = ""; - fOneShot = false; - fClient = false; // set by version message - fInbound = fInboundIn; - fNetworkNode = false; - fSuccessfullyConnected = false; - fDisconnect = false; - nRefCount = 0; - nSendSize = 0; - nSendOffset = 0; - hashContinue = 0; - pindexLastGetBlocksBegin = 0; - hashLastGetBlocksEnd = 0; - nStartingHeight = -1; - fStartSync = false; - fGetAddr = false; - nMisbehavior = 0; - hashCheckpointKnown = 0; - fRelayTxes = false; - setInventoryKnown.max_size(SendBufferSize() / 1000); - pfilter = NULL; - - // Be shy and don't send version until we hear - if (hSocket != INVALID_SOCKET && !fInbound) - PushVersion(); - } - - ~CNode() - { - if (hSocket != INVALID_SOCKET) - { - closesocket(hSocket); - hSocket = INVALID_SOCKET; - } - if (pfilter) - delete pfilter; - } - -private: - CNode(const CNode&); - void operator=(const CNode&); -public: - - - int GetRefCount() - { - assert(nRefCount >= 0); - return nRefCount; - } - - // requires LOCK(cs_vRecvMsg) - unsigned int GetTotalRecvSize() - { - unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) - total += msg.vRecv.size() + 24; - return total; - } - - // requires LOCK(cs_vRecvMsg) - bool ReceiveMsgBytes(const char *pch, unsigned int nBytes); - - // requires LOCK(cs_vRecvMsg) - void SetRecvVersion(int nVersionIn) - { - nRecvVersion = nVersionIn; - BOOST_FOREACH(CNetMessage &msg, vRecvMsg) - msg.SetVersion(nVersionIn); - } - - CNode* AddRef() - { - nRefCount++; - return this; - } - - void Release() - { - nRefCount--; - } - - - - void AddAddressKnown(const CAddress& addr) - { - setAddrKnown.insert(addr); - } - - void PushAddress(const CAddress& addr) - { - // Known checking here is only to save space from duplicates. - // SendMessages will filter it again for knowns that were added - // after addresses were pushed. - if (addr.IsValid() && !setAddrKnown.count(addr)) - vAddrToSend.push_back(addr); - } - - - void AddInventoryKnown(const CInv& inv) - { - { - LOCK(cs_inventory); - setInventoryKnown.insert(inv); - } - } - - void PushInventory(const CInv& inv) - { - { - LOCK(cs_inventory); - if (!setInventoryKnown.count(inv)) - vInventoryToSend.push_back(inv); - } - } - - void AskFor(const CInv& inv) - { - // We're using mapAskFor as a priority queue, - // the key is the earliest time the request can be sent - int64 nRequestTime; - limitedmap::const_iterator it = mapAlreadyAskedFor.find(inv); - if (it != mapAlreadyAskedFor.end()) - nRequestTime = it->second; - else - nRequestTime = 0; - if (fDebugNet) - printf("askfor %s %"PRI64d" (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); - - // Make sure not to reuse time indexes to keep things in the same order - int64 nNow = (GetTime() - 1) * 1000000; - static int64 nLastTime; - ++nLastTime; - nNow = std::max(nNow, nLastTime); - nLastTime = nNow; - - // Each retry is 2 minutes after the last - nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); - if (it != mapAlreadyAskedFor.end()) - mapAlreadyAskedFor.update(it, nRequestTime); - else - mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); - mapAskFor.insert(std::make_pair(nRequestTime, inv)); - } - - - - // TODO: Document the postcondition of this function. Is cs_vSend locked? - void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) - { - ENTER_CRITICAL_SECTION(cs_vSend); - assert(ssSend.size() == 0); - ssSend << CMessageHeader(pszCommand, 0); - if (fDebug) - printf("sending: %s ", pszCommand); - } - - // TODO: Document the precondition of this function. Is cs_vSend locked? - void AbortMessage() UNLOCK_FUNCTION(cs_vSend) - { - ssSend.clear(); - - LEAVE_CRITICAL_SECTION(cs_vSend); - - if (fDebug) - printf("(aborted)\n"); - } - - // TODO: Document the precondition of this function. Is cs_vSend locked? - void EndMessage() UNLOCK_FUNCTION(cs_vSend) - { - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - printf("dropmessages DROPPING SEND MESSAGE\n"); - AbortMessage(); - return; - } - - if (ssSend.size() == 0) - return; - - // Set the size - unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; - memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); - - // Set the checksum - uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); - memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); - - if (fDebug) { - printf("(%d bytes)\n", nSize); - } - - std::deque::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); - ssSend.GetAndClear(*it); - nSendSize += (*it).size(); - - // If write queue empty, attempt "optimistic write" - if (it == vSendMsg.begin()) - SocketSendData(this); - - LEAVE_CRITICAL_SECTION(cs_vSend); - } - - void PushVersion(); - - - void PushMessage(const char* pszCommand) - { - try - { - BeginMessage(pszCommand); - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1) - { - try - { - BeginMessage(pszCommand); - ssSend << a1; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); - bool IsSubscribed(unsigned int nChannel); - void Subscribe(unsigned int nChannel, unsigned int nHops=0); - void CancelSubscribe(unsigned int nChannel); - void CloseSocketDisconnect(); - void Cleanup(); - - - // Denial-of-service detection/prevention - // The idea is to detect peers that are behaving - // badly and disconnect/ban them, but do it in a - // one-coding-mistake-won't-shatter-the-entire-network - // way. - // IMPORTANT: There should be nothing I can give a - // node that it will forward on that will make that - // node's peers drop it. If there is, an attacker - // can isolate a node and/or try to split the network. - // Dropping a node for sending stuff that is invalid - // now but might be valid in a later version is also - // dangerous, because it can cause a network split - // between nodes running old code and nodes running - // new code. - static void ClearBanned(); // needed for unit testing - static bool IsBanned(CNetAddr ip); - bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot - void copyStats(CNodeStats &stats); -}; - - - -class CTransaction; -void RelayTransaction(const CTransaction& tx, const uint256& hash); -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); - -#endif diff --git a/src/netbase.h b/src/netbase.h index e4ec4ef5..9781ac4d 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -7,7 +7,6 @@ #include #include -#include "serialize.h" #include "compat.h" extern int nConnectTimeout; @@ -79,11 +78,6 @@ class CNetAddr friend bool operator==(const CNetAddr& a, const CNetAddr& b); friend bool operator!=(const CNetAddr& a, const CNetAddr& b); friend bool operator<(const CNetAddr& a, const CNetAddr& b); - - IMPLEMENT_SERIALIZE - ( - READWRITE(FLATDATA(ip)); - ) }; /** A combination of a network address (CNetAddr) and a (TCP) port */ @@ -120,15 +114,6 @@ class CService : public CNetAddr CService(const struct sockaddr_in6& addr); #endif - IMPLEMENT_SERIALIZE - ( - CService* pthis = const_cast(this); - READWRITE(FLATDATA(ip)); - unsigned short portN = htons(port); - READWRITE(portN); - if (fRead) - pthis->port = ntohs(portN); - ) }; typedef std::pair proxyType; diff --git a/src/noui.cpp b/src/noui.cpp deleted file mode 100644 index c0e00c47..00000000 --- a/src/noui.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "ui_interface.h" -#include "init.h" -#include "bitcoinrpc.h" - -#include - -static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) -{ - std::string strCaption; - // Check for usage of predefined caption - switch (style) { - case CClientUIInterface::MSG_ERROR: - strCaption += _("Error"); - break; - case CClientUIInterface::MSG_WARNING: - strCaption += _("Warning"); - break; - case CClientUIInterface::MSG_INFORMATION: - strCaption += _("Information"); - break; - default: - strCaption += caption; // Use supplied caption (can be empty) - } - - printf("%s: %s\n", strCaption.c_str(), message.c_str()); - fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); - return false; -} - -static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) -{ - return true; -} - -static void noui_InitMessage(const std::string &message) -{ - printf("init message: %s\n", message.c_str()); -} - -void noui_connect() -{ - // Connect bitcoind signal handlers - uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); - uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee); - uiInterface.InitMessage.connect(noui_InitMessage); -} diff --git a/src/prime.cpp b/src/prime.cpp index 74581123..74531a8f 100644 --- a/src/prime.cpp +++ b/src/prime.cpp @@ -3,6 +3,7 @@ // see the accompanying file COPYING #include "prime.h" +#include #include /**********************/ @@ -792,7 +793,7 @@ bool MineProbablePrimeChain(CBlock& block, mpz_class& mpzFixedMultiplier, bool& bnPrimeChainMultiplier.SetHex(mpzPrimeChainMultiplier.get_str(16)); block.bnPrimeChainMultiplier = bnPrimeChainMultiplier; //printf("nTriedMultiplier = %u\n", nTriedMultiplier); // Debugging - printf("Probable prime chain found for block=%s!!\n Target: %s\n Chain: %s\n", block.GetHash().GetHex().c_str(), + printf("Probable prime chain found\n Target: %s\n Chain: %s\n", TargetToString(block.nBits).c_str(), GetPrimeChainName(nCandidateType, nChainLength).c_str()); nProbableChainLength = nChainLength; return true; diff --git a/src/protocol.cpp b/src/protocol.cpp deleted file mode 100644 index 88bbe49a..00000000 --- a/src/protocol.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "protocol.h" -#include "util.h" -#include "netbase.h" -#include "main.h" - -#ifndef WIN32 -# include -#endif - -static const char* ppszTypeName[] = -{ - "ERROR", - "tx", - "block", - "filtered block" -}; - -CMessageHeader::CMessageHeader() -{ - memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); - memset(pchCommand, 0, sizeof(pchCommand)); - pchCommand[1] = 1; - nMessageSize = -1; - nChecksum = 0; -} - -CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) -{ - memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); - strncpy(pchCommand, pszCommand, COMMAND_SIZE); - nMessageSize = nMessageSizeIn; - nChecksum = 0; -} - -std::string CMessageHeader::GetCommand() const -{ - if (pchCommand[COMMAND_SIZE-1] == 0) - return std::string(pchCommand, pchCommand + strlen(pchCommand)); - else - return std::string(pchCommand, pchCommand + COMMAND_SIZE); -} - -bool CMessageHeader::IsValid() const -{ - // Check start string - if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) - return false; - - // Check the command string for errors - for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) - { - if (*p1 == 0) - { - // Must be all zeros after the first zero - for (; p1 < pchCommand + COMMAND_SIZE; p1++) - if (*p1 != 0) - return false; - } - else if (*p1 < ' ' || *p1 > 0x7E) - return false; - } - - // Message size - if (nMessageSize > MAX_SIZE) - { - printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); - return false; - } - - return true; -} - - - -CAddress::CAddress() : CService() -{ - Init(); -} - -CAddress::CAddress(CService ipIn, uint64 nServicesIn) : CService(ipIn) -{ - Init(); - nServices = nServicesIn; -} - -void CAddress::Init() -{ - nServices = NODE_NETWORK; - nTime = 100000000; - nLastTry = 0; -} - -CInv::CInv() -{ - type = 0; - hash = 0; -} - -CInv::CInv(int typeIn, const uint256& hashIn) -{ - type = typeIn; - hash = hashIn; -} - -CInv::CInv(const std::string& strType, const uint256& hashIn) -{ - unsigned int i; - for (i = 1; i < ARRAYLEN(ppszTypeName); i++) - { - if (strType == ppszTypeName[i]) - { - type = i; - break; - } - } - if (i == ARRAYLEN(ppszTypeName)) - throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); - hash = hashIn; -} - -bool operator<(const CInv& a, const CInv& b) -{ - return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); -} - -bool CInv::IsKnownType() const -{ - return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName)); -} - -const char* CInv::GetCommand() const -{ - if (!IsKnownType()) - throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); - return ppszTypeName[type]; -} - -std::string CInv::ToString() const -{ - return strprintf("%s %s", GetCommand(), hash.ToString().c_str()); -} - -void CInv::print() const -{ - printf("CInv(%s)\n", ToString().c_str()); -} - diff --git a/src/protocol.h b/src/protocol.h deleted file mode 100644 index 002e1812..00000000 --- a/src/protocol.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef __cplusplus -# error This header can only be compiled as C++. -#endif - -#ifndef __INCLUDED_PROTOCOL_H__ -#define __INCLUDED_PROTOCOL_H__ - -#include "serialize.h" -#include "netbase.h" -#include -#include "uint256.h" - -#define PRIMECOIN_PORT 9911 -#define RPC_PORT 9912 -#define TESTNET_PORT 9913 -#define TESTNET_RPC_PORT 9914 - -extern bool fTestNet; - -static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) -{ - return testnet ? TESTNET_PORT : PRIMECOIN_PORT; -} - - -extern unsigned char pchMessageStart[4]; - -/** Message header. - * (4) message start. - * (12) command. - * (4) size. - * (4) checksum. - */ -class CMessageHeader -{ - public: - CMessageHeader(); - CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn); - - std::string GetCommand() const; - bool IsValid() const; - - IMPLEMENT_SERIALIZE - ( - READWRITE(FLATDATA(pchMessageStart)); - READWRITE(FLATDATA(pchCommand)); - READWRITE(nMessageSize); - READWRITE(nChecksum); - ) - - // TODO: make private (improves encapsulation) - public: - enum { - MESSAGE_START_SIZE=sizeof(::pchMessageStart), - COMMAND_SIZE=12, - MESSAGE_SIZE_SIZE=sizeof(int), - CHECKSUM_SIZE=sizeof(int), - - MESSAGE_SIZE_OFFSET=MESSAGE_START_SIZE+COMMAND_SIZE, - CHECKSUM_OFFSET=MESSAGE_SIZE_OFFSET+MESSAGE_SIZE_SIZE, - HEADER_SIZE=MESSAGE_START_SIZE+COMMAND_SIZE+MESSAGE_SIZE_SIZE+CHECKSUM_SIZE - }; - char pchMessageStart[MESSAGE_START_SIZE]; - char pchCommand[COMMAND_SIZE]; - unsigned int nMessageSize; - unsigned int nChecksum; -}; - -/** nServices flags */ -enum -{ - NODE_NETWORK = (1 << 0), -}; - -/** A CService with information about it as peer */ -class CAddress : public CService -{ - public: - CAddress(); - explicit CAddress(CService ipIn, uint64 nServicesIn=NODE_NETWORK); - - void Init(); - - IMPLEMENT_SERIALIZE - ( - CAddress* pthis = const_cast(this); - CService* pip = (CService*)pthis; - if (fRead) - pthis->Init(); - if (nType & SER_DISK) - READWRITE(nVersion); - if ((nType & SER_DISK) || - (nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH))) - READWRITE(nTime); - READWRITE(nServices); - READWRITE(*pip); - ) - - void print() const; - - // TODO: make private (improves encapsulation) - public: - uint64 nServices; - - // disk and network only - unsigned int nTime; - - // memory only - int64 nLastTry; -}; - -/** inv message data */ -class CInv -{ - public: - CInv(); - CInv(int typeIn, const uint256& hashIn); - CInv(const std::string& strType, const uint256& hashIn); - - IMPLEMENT_SERIALIZE - ( - READWRITE(type); - READWRITE(hash); - ) - - friend bool operator<(const CInv& a, const CInv& b); - - bool IsKnownType() const; - const char* GetCommand() const; - std::string ToString() const; - void print() const; - - // TODO: make private (improves encapsulation) - public: - int type; - uint256 hash; -}; - -enum -{ - MSG_TX = 1, - MSG_BLOCK, - // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however, - // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata. - MSG_FILTERED_BLOCK, -}; - -#endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp deleted file mode 100644 index 1eb094dc..00000000 --- a/src/rpcblockchain.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#include "main.h" -#include "bitcoinrpc.h" -#include "prime.h" -#include "wallet.h" -#include "init.h" - -using namespace json_spirit; -using namespace std; - -void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out); - -// Primecoin: get prime difficulty value (chain length) -double GetDifficulty(const CBlockIndex* blockindex) -{ - // Floating point number that is approximate log scale of prime target, - // minimum difficulty = 256, maximum difficulty = 2039 - if (blockindex == NULL) - { - if (pindexBest == NULL) - return 256.0; - else - blockindex = pindexBest; - } - - double dDiff = GetPrimeDifficulty(blockindex->nBits); - return dDiff; -} - -Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) -{ - Object result; - result.push_back(Pair("hash", block.GetHash().GetHex())); - CMerkleTx txGen(block.vtx[0]); - txGen.SetMerkleBranch(&block); - result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - Array txs; - BOOST_FOREACH(const CTransaction&tx, block.vtx) - txs.push_back(tx.GetHash().GetHex()); - result.push_back(Pair("tx", txs)); - result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime())); - result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); - result.push_back(Pair("bits", HexBits(block.nBits))); - result.push_back(Pair("difficulty", GetPrimeDifficulty(block.nBits))); - result.push_back(Pair("transition", GetPrimeDifficulty(blockindex->nWorkTransition))); - CBigNum bnPrimeChainOrigin = CBigNum(block.GetHeaderHash()) * block.bnPrimeChainMultiplier; - result.push_back(Pair("primechain", GetPrimeChainName(blockindex->nPrimeChainType, blockindex->nPrimeChainLength).c_str())); - result.push_back(Pair("primeorigin", bnPrimeChainOrigin.ToString().c_str())); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - if (blockindex->pnext) - result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); - return result; -} - - -Value getblockcount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "Returns the number of blocks in the longest block chain."); - - return nBestHeight; -} - - -Value getdifficulty(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "Returns the proof-of-work difficulty in prime chain length."); - - return GetDifficulty(); -} - - -Value settxfee(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE) - throw runtime_error( - "settxfee \n" - " is a real and is rounded to 0.01 (cent)\n" - "Minimum and default transaction fee per KB is 1 cent"); - - nTransactionFee = (AmountFromValue(params[0]) / CENT) * CENT; // round to cent - return true; -} - -Value getrawmempool(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getrawmempool\n" - "Returns all transaction ids in memory pool."); - - vector vtxid; - mempool.queryHashes(vtxid); - - Array a; - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); - - return a; -} - -Value getblockhash(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getblockhash \n" - "Returns hash of block in best-block-chain at ."); - - int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > nBestHeight) - throw runtime_error("Block number out of range."); - - CBlockIndex* pblockindex = FindBlockByHeight(nHeight); - return pblockindex->phashBlock->GetHex(); -} - -Value getblock(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getblock \n" - "Returns details of a block with given block-hash."); - - std::string strHash = params[0].get_str(); - uint256 hash(strHash); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex); - - return blockToJSON(block, pblockindex); -} - -Value gettxoutsetinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gettxoutsetinfo\n" - "Returns statistics about the unspent transaction output set."); - - Object ret; - - CCoinsStats stats; - if (pcoinsTip->GetStats(stats)) { - ret.push_back(Pair("height", (boost::int64_t)stats.nHeight)); - ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); - ret.push_back(Pair("transactions", (boost::int64_t)stats.nTransactions)); - ret.push_back(Pair("txouts", (boost::int64_t)stats.nTransactionOutputs)); - ret.push_back(Pair("bytes_serialized", (boost::int64_t)stats.nSerializedSize)); - ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); - } - return ret; -} - -Value gettxout(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - throw runtime_error( - "gettxout [includemempool=true]\n" - "Returns details about an unspent transaction output."); - - Object ret; - - std::string strHash = params[0].get_str(); - uint256 hash(strHash); - int n = params[1].get_int(); - bool fMempool = true; - if (params.size() > 2) - fMempool = params[2].get_bool(); - - CCoins coins; - if (fMempool) { - LOCK(mempool.cs); - CCoinsViewMemPool view(*pcoinsTip, mempool); - if (!view.GetCoins(hash, coins)) - return Value::null; - mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool - } else { - if (!pcoinsTip->GetCoins(hash, coins)) - return Value::null; - } - if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) - return Value::null; - - ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); - if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) - ret.push_back(Pair("confirmations", 0)); - else - ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); - ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); - Object o; - ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o); - ret.push_back(Pair("scriptPubKey", o)); - ret.push_back(Pair("version", coins.nVersion)); - ret.push_back(Pair("coinbase", coins.fCoinBase)); - - return ret; -} - -// Primecoin: list prime chain records within primecoin network -Value listprimerecords(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "listprimerecords [primechain type]\n" - "Returns the list of record prime chains in primecoin network.\n" - " is integer like 10, 11, 12 etc.\n" - "[primechain type] is optional type, among 1CC, 2CC and TWN"); - - int nPrimeChainLength = params[0].get_int(); - unsigned int nPrimeChainType = 0; - if (params.size() > 1) - { - std::string strPrimeChainType = params[1].get_str(); - if (strPrimeChainType.compare("1CC") == 0) - nPrimeChainType = PRIME_CHAIN_CUNNINGHAM1; - else if (strPrimeChainType.compare("2CC") == 0) - nPrimeChainType = PRIME_CHAIN_CUNNINGHAM2; - else if (strPrimeChainType.compare("TWN") == 0) - nPrimeChainType = PRIME_CHAIN_BI_TWIN; - else - throw runtime_error("Prime chain type must be 1CC, 2CC or TWN."); - } - - Array ret; - - CBigNum bnPrimeRecord = 0; - - for (CBlockIndex* pindex = pindexGenesisBlock; pindex; pindex = pindex->pnext) - { - if (nPrimeChainLength != (int) TargetGetLength(pindex->nPrimeChainLength)) - continue; // length not matching, next block - if (nPrimeChainType && nPrimeChainType != pindex->nPrimeChainType) - continue; // type not matching, next block - - CBlock block; - block.ReadFromDisk(pindex); // read block - CBigNum bnPrimeChainOrigin = CBigNum(block.GetHeaderHash()) * block.bnPrimeChainMultiplier; // compute prime chain origin - - if (bnPrimeChainOrigin > bnPrimeRecord) - { - bnPrimeRecord = bnPrimeChainOrigin; // new record in primecoin - Object entry; - entry.push_back(Pair("time", DateTimeStrFormat("%Y-%m-%d %H:%M:%S UTC", pindex->GetBlockTime()).c_str())); - entry.push_back(Pair("epoch", (boost::int64_t) pindex->GetBlockTime())); - entry.push_back(Pair("height", pindex->nHeight)); - entry.push_back(Pair("ismine", pwalletMain->IsMine(block.vtx[0]))); - entry.push_back(Pair("primedigit", (int) bnPrimeChainOrigin.ToString().length())); - entry.push_back(Pair("primechain", GetPrimeChainName(pindex->nPrimeChainType, pindex->nPrimeChainLength).c_str())); - entry.push_back(Pair("primeorigin", bnPrimeChainOrigin.ToString().c_str())); - entry.push_back(Pair("primorialform", GetPrimeOriginPrimorialForm(bnPrimeChainOrigin).c_str())); - ret.push_back(entry); - } - } - - return ret; -} - -// Primecoin: list top prime chain within primecoin network -Value listtopprimes(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "listtopprimes [primechain type]\n" - "Returns the list of top prime chains in primecoin network.\n" - " is integer like 10, 11, 12 etc.\n" - "[primechain type] is optional type, among 1CC, 2CC and TWN"); - - int nPrimeChainLength = params[0].get_int(); - unsigned int nPrimeChainType = 0; - if (params.size() > 1) - { - std::string strPrimeChainType = params[1].get_str(); - if (strPrimeChainType.compare("1CC") == 0) - nPrimeChainType = PRIME_CHAIN_CUNNINGHAM1; - else if (strPrimeChainType.compare("2CC") == 0) - nPrimeChainType = PRIME_CHAIN_CUNNINGHAM2; - else if (strPrimeChainType.compare("TWN") == 0) - nPrimeChainType = PRIME_CHAIN_BI_TWIN; - else - throw runtime_error("Prime chain type must be 1CC, 2CC or TWN."); - } - - // Search for top prime chains - unsigned int nRankingSize = 10; // ranking list size - unsigned int nSortVectorSize = 64; // vector size for sort operation - CBigNum bnPrimeQualify = 0; // minimum qualify value for ranking list - vector > vSortedByOrigin; - for (CBlockIndex* pindex = pindexGenesisBlock; pindex; pindex = pindex->pnext) - { - if (nPrimeChainLength != (int) TargetGetLength(pindex->nPrimeChainLength)) - continue; // length not matching, next block - if (nPrimeChainType && nPrimeChainType != pindex->nPrimeChainType) - continue; // type not matching, next block - - CBlock block; - block.ReadFromDisk(pindex); // read block - CBigNum bnPrimeChainOrigin = CBigNum(block.GetHeaderHash()) * block.bnPrimeChainMultiplier; // compute prime chain origin - - if (bnPrimeChainOrigin > bnPrimeQualify) - vSortedByOrigin.push_back(make_pair(bnPrimeChainOrigin, block.GetHash())); - - if (vSortedByOrigin.size() >= nSortVectorSize) - { - // Sort prime chain candidates - sort(vSortedByOrigin.begin(), vSortedByOrigin.end()); - reverse(vSortedByOrigin.begin(), vSortedByOrigin.end()); - // Truncate candidate list - while (vSortedByOrigin.size() > nRankingSize) - vSortedByOrigin.pop_back(); - // Update minimum qualify value for top prime chains - bnPrimeQualify = vSortedByOrigin.back().first; - } - } - - // Final sort of prime chain candidates - sort(vSortedByOrigin.begin(), vSortedByOrigin.end()); - reverse(vSortedByOrigin.begin(), vSortedByOrigin.end()); - // Truncate candidate list - while (vSortedByOrigin.size() > nRankingSize) - vSortedByOrigin.pop_back(); - - // Output top prime chains - Array ret; - BOOST_FOREACH(const PAIRTYPE(CBigNum, uint256)& item, vSortedByOrigin) - { - CBigNum bnPrimeChainOrigin = item.first; - CBlockIndex* pindex = mapBlockIndex[item.second]; - CBlock block; - block.ReadFromDisk(pindex); // read block - Object entry; - entry.push_back(Pair("time", DateTimeStrFormat("%Y-%m-%d %H:%M:%S UTC", pindex->GetBlockTime()).c_str())); - entry.push_back(Pair("epoch", (boost::int64_t) pindex->GetBlockTime())); - entry.push_back(Pair("height", pindex->nHeight)); - entry.push_back(Pair("ismine", pwalletMain->IsMine(block.vtx[0]))); - entry.push_back(Pair("primedigit", (int) bnPrimeChainOrigin.ToString().length())); - entry.push_back(Pair("primechain", GetPrimeChainName(pindex->nPrimeChainType, pindex->nPrimeChainLength).c_str())); - entry.push_back(Pair("primeorigin", bnPrimeChainOrigin.ToString().c_str())); - entry.push_back(Pair("primorialform", GetPrimeOriginPrimorialForm(bnPrimeChainOrigin).c_str())); - ret.push_back(entry); - } - - return ret; -} diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp deleted file mode 100644 index 24023920..00000000 --- a/src/rpcdump.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2009-2012 Bitcoin Developers -// Copyright (c) 2013 Primecoin Developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "init.h" // for pwalletMain -#include "bitcoinrpc.h" -#include "ui_interface.h" -#include "base58.h" - -#include - -#define printf OutputDebugStringF - -using namespace json_spirit; -using namespace std; - -class CTxDump -{ -public: - CBlockIndex *pindex; - int64 nValue; - bool fSpent; - CWalletTx* ptx; - int nOut; - CTxDump(CWalletTx* ptx = NULL, int nOut = -1) - { - pindex = NULL; - nValue = 0; - fSpent = false; - this->ptx = ptx; - this->nOut = nOut; - } -}; - -Value importprivkey(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 3) - throw runtime_error( - "importprivkey [label] [rescan=true]\n" - "Adds a private key (as returned by dumpprivkey) to your wallet."); - - string strSecret = params[0].get_str(); - string strLabel = ""; - if (params.size() > 1) - strLabel = params[1].get_str(); - - // Whether to perform rescan after import - bool fRescan = true; - if (params.size() > 2) - fRescan = params[2].get_bool(); - - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(strSecret); - - if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); - - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); - CKeyID vchAddress = key.GetPubKey().GetID(); - { - LOCK2(cs_main, pwalletMain->cs_wallet); - - pwalletMain->MarkDirty(); - pwalletMain->SetAddressBookName(vchAddress, strLabel); - - if (!pwalletMain->AddKey(key)) - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); - - if (fRescan) { - pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); - pwalletMain->ReacceptWalletTransactions(); - } - } - - return Value::null; -} - -Value dumpprivkey(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "dumpprivkey \n" - "Reveals the private key corresponding to ."); - - string strAddress = params[0].get_str(); - CBitcoinAddress address; - if (!address.SetString(strAddress)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Primecoin address"); - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); - CSecret vchSecret; - bool fCompressed; - if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed)) - throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); - return CBitcoinSecret(vchSecret, fCompressed).ToString(); -} diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp deleted file mode 100644 index d8e6f6b7..00000000 --- a/src/rpcmining.cpp +++ /dev/null @@ -1,448 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 The Primecoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "main.h" -#include "db.h" -#include "init.h" -#include "bitcoinrpc.h" -#include "prime.h" - -using namespace json_spirit; -using namespace std; - -Value getgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getgenerate\n" - "Returns true or false."); - - return GetBoolArg("-gen"); -} - - -Value setgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setgenerate [genproclimit]\n" - " is true or false to turn generation on or off.\n" - "Generation is limited to [genproclimit] processors, -1 is unlimited."); - - bool fGenerate = true; - if (params.size() > 0) - fGenerate = params[0].get_bool(); - - if (params.size() > 1) - { - int nGenProcLimit = params[1].get_int(); - mapArgs["-genproclimit"] = itostr(nGenProcLimit); - if (nGenProcLimit == 0) - fGenerate = false; - } - mapArgs["-gen"] = (fGenerate ? "1" : "0"); - - GenerateBitcoins(fGenerate, pwalletMain); - return Value::null; -} - - -Value getsievepercentage(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getsievepercentage\n" - "Returns the current sieve percentage used by the mining algorithm."); - - return (boost::int64_t)nSievePercentage; -} - - -Value setsievepercentage(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1) - throw runtime_error( - "setsievepercentage \n" - " determines how many rounds the candidate multiplier sieve runs."); - - unsigned int nPercentage = nDefaultSievePercentage; - if (params.size() > 0) - nPercentage = params[0].get_int(); - - nPercentage = std::max(std::min(nPercentage, nMaxSievePercentage), nMinSievePercentage); - - nSievePercentage = nPercentage; - return Value::null; -} - - -Value getsieveextensions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getsieveextensions\n" - "Returns the number of times the sieve is extended."); - - return (boost::int64_t)nSieveExtensions; -} - - -Value setsieveextensions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1) - throw runtime_error( - "setsieveextensions \n" - " determines the number of times the sieve will be extended."); - - unsigned int nExtensions = (fTestNet) ? nDefaultSieveExtensionsTestnet : nDefaultSieveExtensions; - if (params.size() > 0) - nExtensions = params[0].get_int(); - - nExtensions = std::max(std::min(nExtensions, nMaxSieveExtensions), nMinSieveExtensions); - - nSieveExtensions = nExtensions; - return Value::null; -} - - -Value getprimespersec(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getprimespersec\n" - "Returns a recent primes per second performance measurement while generating."); - - return (boost::int64_t)dPrimesPerSec; -} - - -Value getchainspermin(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getchainspermin\n" - "Returns a recent chains per second performance measurement while generating."); - - return (boost::int64_t)dChainsPerMinute; -} - - -extern Value getdifficulty(const Array& params, bool fHelp); - - -Value getmininginfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getmininginfo\n" - "Returns an object containing mining-related information."); - - Object obj; - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("chainspermin", getchainspermin(params, false))); - obj.push_back(Pair("chainsperday", dChainsPerDay)); - obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); - obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx)); - obj.push_back(Pair("difficulty", getdifficulty(params, false))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", GetBoolArg("-gen"))); - obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); - obj.push_back(Pair("primespersec", getprimespersec(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("sieveextensions",(int)nSieveExtensions)); - obj.push_back(Pair("sievepercentage",(int)nSievePercentage)); - obj.push_back(Pair("sievesize", (int)nSieveSize)); - obj.push_back(Pair("testnet", fTestNet)); - return obj; -} - - -Value getwork(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getwork [data]\n" - "If [data] is not specified, returns formatted hash data to work on:\n" - " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated - " \"data\" : block data\n" - " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated - " \"target\" : little endian hash target\n" - "If [data] is specified, tries to solve the block and returns true if it was successful."); - - if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Primecoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Primecoin is downloading blocks..."); - - typedef map > mapNewBlock_t; - static mapNewBlock_t mapNewBlock; // FIXME: thread safety - static vector vNewBlockTemplate; - - if (params.size() == 0) - { - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) - { - if (pindexPrev != pindexBest) - { - // Deallocate old blocks since they're obsolete now - mapNewBlock.clear(); - BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) - delete pblocktemplate; - vNewBlockTemplate.clear(); - } - - // Clear pindexPrev so future getworks make a new block, despite any failures from here on - pindexPrev = NULL; - - // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; - nStart = GetTime(); - - // Create new block - pblocktemplate = CreateNewBlock(*pMiningKey); - if (!pblocktemplate) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - vNewBlockTemplate.push_back(pblocktemplate); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; - } - CBlock* pblock = &pblocktemplate->block; // pointer for convenience - - // Update nTime - pblock->UpdateTime(pindexPrev); - pblock->nNonce = 0; - - // Update nExtraNonce - static unsigned int nExtraNonce = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - - // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); - - // Pre-build hash buffers - char pmidstate[32]; - char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated - result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated - result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - return result; - } - else - { - // Parse parameters - vector vchData = ParseHex(params[0].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); - CBlock* pdata = (CBlock*)&vchData[0]; - - // Byte reverse - for (int i = 0; i < 128/4; i++) - ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); - - // Get saved block - if (!mapNewBlock.count(pdata->hashMerkleRoot)) - return false; - CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; - - pblock->nTime = pdata->nTime; - pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - - return CheckWork(pblock, *pwalletMain, *pMiningKey); - } -} - - -Value getblocktemplate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getblocktemplate [params]\n" - "Returns data needed to construct a block to work on:\n" - " \"version\" : block version\n" - " \"previousblockhash\" : hash of current highest block\n" - " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n" - " \"coinbaseaux\" : data that should be included in coinbase\n" - " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" - " \"target\" : hash target\n" - " \"mintime\" : minimum timestamp appropriate for next block\n" - " \"curtime\" : current timestamp\n" - " \"mutable\" : list of ways the block template may be changed\n" - " \"noncerange\" : range of valid nonces\n" - " \"sigoplimit\" : limit of sigops in blocks\n" - " \"sizelimit\" : limit of block size\n" - " \"bits\" : compressed target of next block\n" - " \"height\" : height of the next block\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); - - std::string strMode = "template"; - if (params.size() > 0) - { - const Object& oparam = params[0].get_obj(); - const Value& modeval = find_value(oparam, "mode"); - if (modeval.type() == str_type) - strMode = modeval.get_str(); - else if (modeval.type() == null_type) - { - /* Do nothing */ - } - else - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - } - - if (strMode != "template") - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - - if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Primecoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Primecoin is downloading blocks..."); - - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) - { - // Clear pindexPrev so future calls make a new block, despite any failures from here on - pindexPrev = NULL; - - // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; - nStart = GetTime(); - - // Create new block - if(pblocktemplate) - { - delete pblocktemplate; - pblocktemplate = NULL; - } - pblocktemplate = CreateNewBlock(*pMiningKey); - if (!pblocktemplate) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; - } - CBlock* pblock = &pblocktemplate->block; // pointer for convenience - - // Update nTime - pblock->UpdateTime(pindexPrev); - pblock->nNonce = 0; - - Array transactions; - map setTxIndex; - int i = 0; - BOOST_FOREACH (CTransaction& tx, pblock->vtx) - { - uint256 txHash = tx.GetHash(); - setTxIndex[txHash] = i++; - - if (tx.IsCoinBase()) - continue; - - Object entry; - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << tx; - entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); - - entry.push_back(Pair("hash", txHash.GetHex())); - - Array deps; - BOOST_FOREACH (const CTxIn &in, tx.vin) - { - if (setTxIndex.count(in.prevout.hash)) - deps.push_back(setTxIndex[in.prevout.hash]); - } - entry.push_back(Pair("depends", deps)); - - int index_in_template = i - 1; - entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); - entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); - - transactions.push_back(entry); - } - - Object aux; - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - static Array aMutable; - if (aMutable.empty()) - { - aMutable.push_back("time"); - aMutable.push_back("transactions"); - aMutable.push_back("prevblock"); - } - - Object result; - result.push_back(Pair("version", pblock->nVersion)); - result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); - result.push_back(Pair("transactions", transactions)); - result.push_back(Pair("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); - result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("mutable", aMutable)); - result.push_back(Pair("noncerange", "00000000ffffffff")); - result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); - result.push_back(Pair("curtime", (int64_t)pblock->nTime)); - result.push_back(Pair("bits", HexBits(pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); - - return result; -} - -Value submitblock(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "submitblock [optional-params-obj]\n" - "[optional-params-obj] parameter is currently ignored.\n" - "Attempts to submit new block to network.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); - - vector blockData(ParseHex(params[0].get_str())); - CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); - CBlock pblock; - try { - ssBlock >> pblock; - } - catch (std::exception &e) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); - } - - CValidationState state; - bool fAccepted = ProcessBlock(state, NULL, &pblock); - if (!fAccepted) - return "rejected"; // TODO: report validation state - - return Value::null; -} diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp deleted file mode 100644 index 6d97c298..00000000 --- a/src/rpcnet.cpp +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) 2009-2012 Bitcoin Developers -// Copyright (c) 2012-2013 PPCoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "net.h" -#include "bitcoinrpc.h" -#include "alert.h" -#include "base58.h" - -using namespace json_spirit; -using namespace std; - -Value getconnectioncount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getconnectioncount\n" - "Returns the number of connections to other nodes."); - - LOCK(cs_vNodes); - return (int)vNodes.size(); -} - -static void CopyNodeStats(std::vector& vstats) -{ - vstats.clear(); - - LOCK(cs_vNodes); - vstats.reserve(vNodes.size()); - BOOST_FOREACH(CNode* pnode, vNodes) { - CNodeStats stats; - pnode->copyStats(stats); - vstats.push_back(stats); - } -} - -Value getpeerinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getpeerinfo\n" - "Returns data about each connected network node."); - - vector vstats; - CopyNodeStats(vstats); - - Array ret; - - BOOST_FOREACH(const CNodeStats& stats, vstats) { - Object obj; - - obj.push_back(Pair("addr", stats.addrName)); - obj.push_back(Pair("services", strprintf("%08"PRI64x, stats.nServices))); - obj.push_back(Pair("lastsend", (boost::int64_t)stats.nLastSend)); - obj.push_back(Pair("lastrecv", (boost::int64_t)stats.nLastRecv)); - obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes)); - obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes)); - obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected)); - obj.push_back(Pair("version", stats.nVersion)); - obj.push_back(Pair("subver", stats.strSubVer)); - obj.push_back(Pair("inbound", stats.fInbound)); - obj.push_back(Pair("startingheight", stats.nStartingHeight)); - obj.push_back(Pair("banscore", stats.nMisbehavior)); - if (stats.fSyncNode) - obj.push_back(Pair("syncnode", true)); - - ret.push_back(obj); - } - - return ret; -} - -Value addnode(const Array& params, bool fHelp) -{ - string strCommand; - if (params.size() == 2) - strCommand = params[1].get_str(); - if (fHelp || params.size() != 2 || - (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) - throw runtime_error( - "addnode \n" - "Attempts add or remove from the addnode list or try a connection to once."); - - string strNode = params[0].get_str(); - - if (strCommand == "onetry") - { - CAddress addr; - ConnectNode(addr, strNode.c_str()); - return Value::null; - } - - LOCK(cs_vAddedNodes); - vector::iterator it = vAddedNodes.begin(); - for(; it != vAddedNodes.end(); it++) - if (strNode == *it) - break; - - if (strCommand == "add") - { - if (it != vAddedNodes.end()) - throw JSONRPCError(-23, "Error: Node already added"); - vAddedNodes.push_back(strNode); - } - else if(strCommand == "remove") - { - if (it == vAddedNodes.end()) - throw JSONRPCError(-24, "Error: Node has not been added."); - vAddedNodes.erase(it); - } - - return Value::null; -} - -Value getaddednodeinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getaddednodeinfo [node]\n" - "Returns information about the given added node, or all added nodes\n" - "(note that onetry addnodes are not listed here)\n" - "If dns is false, only a list of added nodes will be provided,\n" - "otherwise connected information will also be available."); - - bool fDns = params[0].get_bool(); - - list laddedNodes(0); - if (params.size() == 1) - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) - laddedNodes.push_back(strAddNode); - } - else - { - string strNode = params[1].get_str(); - LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) - if (strAddNode == strNode) - { - laddedNodes.push_back(strAddNode); - break; - } - if (laddedNodes.size() == 0) - throw JSONRPCError(-24, "Error: Node has not been added."); - } - - if (!fDns) - { - Object ret; - BOOST_FOREACH(string& strAddNode, laddedNodes) - ret.push_back(Pair("addednode", strAddNode)); - return ret; - } - - Array ret; - - list > > laddedAddreses(0); - BOOST_FOREACH(string& strAddNode, laddedNodes) - { - vector vservNode(0); - if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) - laddedAddreses.push_back(make_pair(strAddNode, vservNode)); - else - { - Object obj; - obj.push_back(Pair("addednode", strAddNode)); - obj.push_back(Pair("connected", false)); - Array addresses; - obj.push_back(Pair("addresses", addresses)); - } - } - - LOCK(cs_vNodes); - for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) - { - Object obj; - obj.push_back(Pair("addednode", it->first)); - - Array addresses; - bool fConnected = false; - BOOST_FOREACH(CService& addrNode, it->second) - { - bool fFound = false; - Object node; - node.push_back(Pair("address", addrNode.ToString())); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->addr == addrNode) - { - fFound = true; - fConnected = true; - node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); - break; - } - if (!fFound) - node.push_back(Pair("connected", "false")); - addresses.push_back(node); - } - obj.push_back(Pair("connected", fConnected)); - obj.push_back(Pair("addresses", addresses)); - ret.push_back(obj); - } - - return ret; -} - - -// make a public-private key pair (first introduced in ppcoin) -Value makekeypair(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "makekeypair [prefix]\n" - "Make a public/private key pair.\n" - "[prefix] is optional preferred prefix for the public key.\n"); - - string strPrefix = ""; - if (params.size() > 0) - strPrefix = params[0].get_str(); - - CKey key; - int nCount = 0; - do - { - key.MakeNewKey(false); - nCount++; - } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey().Raw()).substr(0, strPrefix.size())); - - if (strPrefix != HexStr(key.GetPubKey().Raw()).substr(0, strPrefix.size())) - return Value::null; - - bool fCompressed; - CSecret vchSecret = key.GetSecret(fCompressed); - Object result; - result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw()))); - result.push_back(Pair("PrivateKey", CBitcoinSecret(vchSecret, fCompressed).ToString())); - return result; -} - - -// Send alert (first introduced in ppcoin) -// There is a known deadlock situation with ThreadMessageHandler -// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages() -// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage() -Value sendalert(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 6) - throw runtime_error( - "sendalert [cancelupto]\n" - " is the alert text message\n" - " is base58 hex string of alert master private key\n" - " is the minimum applicable internal client version\n" - " is the maximum applicable internal client version\n" - " is integer priority number\n" - " is the alert id\n" - "[cancelupto] cancels all alert id's up to this number\n" - "Returns true or false."); - - // Prepare the alert message - CAlert alert; - alert.strStatusBar = params[0].get_str(); - alert.nMinVer = params[2].get_int(); - alert.nMaxVer = params[3].get_int(); - alert.nPriority = params[4].get_int(); - alert.nID = params[5].get_int(); - if (params.size() > 6) - alert.nCancel = params[6].get_int(); - alert.nVersion = PROTOCOL_VERSION; - alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60; - alert.nExpiration = GetAdjustedTime() + 365*24*60*60; - - CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); - sMsg << (CUnsignedAlert)alert; - alert.vchMsg = vector(sMsg.begin(), sMsg.end()); - - // Prepare master key and sign alert message - CBitcoinSecret vchSecret; - if (!vchSecret.SetString(params[1].get_str())) - throw runtime_error("Invalid alert master key"); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); // if key is not correct openssl may crash - if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) - throw runtime_error( - "Unable to sign alert, check alert master key?\n"); - - // Process alert - if(!alert.ProcessAlert()) - throw runtime_error( - "Failed to process alert.\n"); - - // Relay alert - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); - } - - Object result; - result.push_back(Pair("strStatusBar", alert.strStatusBar)); - result.push_back(Pair("nVersion", alert.nVersion)); - result.push_back(Pair("nMinVer", alert.nMinVer)); - result.push_back(Pair("nMaxVer", alert.nMaxVer)); - result.push_back(Pair("nPriority", alert.nPriority)); - result.push_back(Pair("nID", alert.nID)); - if (alert.nCancel > 0) - result.push_back(Pair("nCancel", alert.nCancel)); - return result; -} - diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp deleted file mode 100644 index fb9811b9..00000000 --- a/src/rpcrawtransaction.cpp +++ /dev/null @@ -1,576 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include - -#include "base58.h" -#include "bitcoinrpc.h" -#include "db.h" -#include "init.h" -#include "main.h" -#include "net.h" -#include "wallet.h" - -using namespace std; -using namespace boost; -using namespace boost::assign; -using namespace json_spirit; - -// -// Utilities: convert hex-encoded Values -// (throws error if not hex). -// -uint256 ParseHashV(const Value& v, string strName) -{ - string strHex; - if (v.type() == str_type) - strHex = v.get_str(); - if (!IsHex(strHex)) // Note: IsHex("") is false - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - uint256 result; - result.SetHex(strHex); - return result; -} -uint256 ParseHashO(const Object& o, string strKey) -{ - return ParseHashV(find_value(o, strKey), strKey); -} -vector ParseHexV(const Value& v, string strName) -{ - string strHex; - if (v.type() == str_type) - strHex = v.get_str(); - if (!IsHex(strHex)) - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - return ParseHex(strHex); -} -vector ParseHexO(const Object& o, string strKey) -{ - return ParseHexV(find_value(o, strKey), strKey); -} - -void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) -{ - txnouttype type; - vector addresses; - int nRequired; - - out.push_back(Pair("asm", scriptPubKey.ToString())); - out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - - if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) - { - out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD))); - return; - } - - out.push_back(Pair("reqSigs", nRequired)); - out.push_back(Pair("type", GetTxnOutputType(type))); - - Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - out.push_back(Pair("addresses", a)); -} - -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) -{ - entry.push_back(Pair("txid", tx.GetHash().GetHex())); - entry.push_back(Pair("version", tx.nVersion)); - entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime)); - Array vin; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - Object in; - if (tx.IsCoinBase()) - in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - else - { - in.push_back(Pair("txid", txin.prevout.hash.GetHex())); - in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n)); - Object o; - o.push_back(Pair("asm", txin.scriptSig.ToString())); - o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - in.push_back(Pair("scriptSig", o)); - } - in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence)); - vin.push_back(in); - } - entry.push_back(Pair("vin", vin)); - Array vout; - for (unsigned int i = 0; i < tx.vout.size(); i++) - { - const CTxOut& txout = tx.vout[i]; - Object out; - out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - out.push_back(Pair("n", (boost::int64_t)i)); - Object o; - ScriptPubKeyToJSON(txout.scriptPubKey, o); - out.push_back(Pair("scriptPubKey", o)); - vout.push_back(out); - } - entry.push_back(Pair("vout", vout)); - - if (hashBlock != 0) - { - entry.push_back(Pair("blockhash", hashBlock.GetHex())); - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - { - entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); - entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); - entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); - } - else - entry.push_back(Pair("confirmations", 0)); - } - } -} - -Value getrawtransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getrawtransaction [verbose=0]\n" - "If verbose=0, returns a string that is\n" - "serialized, hex-encoded data for .\n" - "If verbose is non-zero, returns an Object\n" - "with information about ."); - - uint256 hash = ParseHashV(params[0], "parameter 1"); - - bool fVerbose = false; - if (params.size() > 1) - fVerbose = (params[1].get_int() != 0); - - CTransaction tx; - uint256 hashBlock = 0; - if (!GetTransaction(hash, tx, hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << tx; - string strHex = HexStr(ssTx.begin(), ssTx.end()); - - if (!fVerbose) - return strHex; - - Object result; - result.push_back(Pair("hex", strHex)); - TxToJSON(tx, hashBlock, result); - return result; -} - -Value listunspent(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 3) - throw runtime_error( - "listunspent [minconf=1] [maxconf=9999999] [\"address\",...]\n" - "Returns array of unspent transaction outputs\n" - "with between minconf and maxconf (inclusive) confirmations.\n" - "Optionally filtered to only include txouts paid to specified addresses.\n" - "Results are an array of Objects, each of which has:\n" - "{txid, vout, scriptPubKey, amount, confirmations}"); - - RPCTypeCheck(params, list_of(int_type)(int_type)(array_type)); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - int nMaxDepth = 9999999; - if (params.size() > 1) - nMaxDepth = params[1].get_int(); - - set setAddress; - if (params.size() > 2) - { - Array inputs = params[2].get_array(); - BOOST_FOREACH(Value& input, inputs) - { - CBitcoinAddress address(input.get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); - setAddress.insert(address); - } - } - - Array results; - vector vecOutputs; - pwalletMain->AvailableCoins(vecOutputs, false); - BOOST_FOREACH(const COutput& out, vecOutputs) - { - if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) - continue; - - if (setAddress.size()) - { - CTxDestination address; - if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) - continue; - - if (!setAddress.count(address)) - continue; - } - - int64 nValue = out.tx->vout[out.i].nValue; - const CScript& pk = out.tx->vout[out.i].scriptPubKey; - Object entry; - entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); - entry.push_back(Pair("vout", out.i)); - CTxDestination address; - if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) - { - entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); - if (pwalletMain->mapAddressBook.count(address)) - entry.push_back(Pair("account", pwalletMain->mapAddressBook[address])); - } - entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); - if (pk.IsPayToScriptHash()) - { - CTxDestination address; - if (ExtractDestination(pk, address)) - { - const CScriptID& hash = boost::get(address); - CScript redeemScript; - if (pwalletMain->GetCScript(hash, redeemScript)) - entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); - } - } - entry.push_back(Pair("amount",ValueFromAmount(nValue))); - entry.push_back(Pair("confirmations",out.nDepth)); - results.push_back(entry); - } - - return results; -} - -Value createrawtransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 2) - throw runtime_error( - "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n" - "Create a transaction spending given inputs\n" - "(array of objects containing transaction id and output number),\n" - "sending to given address(es).\n" - "Returns hex-encoded raw transaction.\n" - "Note that the transaction's inputs are not signed, and\n" - "it is not stored in the wallet or transmitted to the network."); - - RPCTypeCheck(params, list_of(array_type)(obj_type)); - - Array inputs = params[0].get_array(); - Object sendTo = params[1].get_obj(); - - CTransaction rawTx; - - BOOST_FOREACH(const Value& input, inputs) - { - const Object& o = input.get_obj(); - - uint256 txid = ParseHashO(o, "txid"); - - const Value& vout_v = find_value(o, "vout"); - if (vout_v.type() != int_type) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); - int nOutput = vout_v.get_int(); - if (nOutput < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - - CTxIn in(COutPoint(txid, nOutput)); - rawTx.vin.push_back(in); - } - - set setAddress; - BOOST_FOREACH(const Pair& s, sendTo) - { - CBitcoinAddress address(s.name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); - - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); - setAddress.insert(address); - - CScript scriptPubKey; - scriptPubKey.SetDestination(address.Get()); - int64 nAmount = AmountFromValue(s.value_); - - CTxOut out(nAmount, scriptPubKey); - rawTx.vout.push_back(out); - } - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - return HexStr(ss.begin(), ss.end()); -} - -Value decoderawtransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "decoderawtransaction \n" - "Return a JSON object representing the serialized, hex-encoded transaction."); - - vector txData(ParseHexV(params[0], "argument")); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - CTransaction tx; - try { - ssData >> tx; - } - catch (std::exception &e) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - } - - Object result; - TxToJSON(tx, 0, result); - - return result; -} - -Value signrawtransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 4) - throw runtime_error( - "signrawtransaction [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [,...] [sighashtype=\"ALL\"]\n" - "Sign inputs for raw transaction (serialized, hex-encoded).\n" - "Second optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the block chain.\n" - "Third optional argument (may be null) is an array of base58-encoded private\n" - "keys that, if given, will be the only keys used to sign the transaction.\n" - "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n" - "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n" - "Returns json object with keys:\n" - " hex : raw transaction with signature(s) (hex-encoded string)\n" - " complete : 1 if transaction has a complete set of signature (0 if not)" - + HelpRequiringPassphrase()); - - RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); - - vector txData(ParseHexV(params[0], "argument 1")); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - vector txVariants; - while (!ssData.empty()) - { - try { - CTransaction tx; - ssData >> tx; - txVariants.push_back(tx); - } - catch (std::exception &e) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - } - } - - if (txVariants.empty()) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); - - // mergedTx will end up with all the signatures; it - // starts as a clone of the rawtx: - CTransaction mergedTx(txVariants[0]); - bool fComplete = true; - - // Fetch previous transactions (inputs): - CCoinsView viewDummy; - CCoinsViewCache view(viewDummy); - { - LOCK(mempool.cs); - CCoinsViewCache &viewChain = *pcoinsTip; - CCoinsViewMemPool viewMempool(viewChain, mempool); - view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view - - BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { - const uint256& prevHash = txin.prevout.hash; - CCoins coins; - view.GetCoins(prevHash, coins); // this is certainly allowed to fail - } - - view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long - } - - bool fGivenKeys = false; - CBasicKeyStore tempKeystore; - if (params.size() > 2 && params[2].type() != null_type) - { - fGivenKeys = true; - Array keys = params[2].get_array(); - BOOST_FOREACH(Value k, keys) - { - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(k.get_str()); - if (!fGood) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); - tempKeystore.AddKey(key); - } - } - else - EnsureWalletIsUnlocked(); - - // Add previous txouts given in the RPC call: - if (params.size() > 1 && params[1].type() != null_type) - { - Array prevTxs = params[1].get_array(); - BOOST_FOREACH(Value& p, prevTxs) - { - if (p.type() != obj_type) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); - - Object prevOut = p.get_obj(); - - RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); - - uint256 txid = ParseHashO(prevOut, "txid"); - - int nOut = find_value(prevOut, "vout").get_int(); - if (nOut < 0) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); - - vector pkData(ParseHexO(prevOut, "scriptPubKey")); - CScript scriptPubKey(pkData.begin(), pkData.end()); - - CCoins coins; - if (view.GetCoins(txid, coins)) { - if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) { - string err("Previous output scriptPubKey mismatch:\n"); - err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ - scriptPubKey.ToString(); - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); - } - // what todo if txid is known, but the actual output isn't? - } - if ((unsigned int)nOut >= coins.vout.size()) - coins.vout.resize(nOut+1); - coins.vout[nOut].scriptPubKey = scriptPubKey; - coins.vout[nOut].nValue = 0; // we don't know the actual output value - view.SetCoins(txid, coins); - - // if redeemScript given and not using the local wallet (private keys - // given), add redeemScript to the tempKeystore so it can be signed: - if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) - { - RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type)); - Value v = find_value(prevOut, "redeemScript"); - if (!(v == Value::null)) - { - vector rsData(ParseHexV(v, "redeemScript")); - CScript redeemScript(rsData.begin(), rsData.end()); - tempKeystore.AddCScript(redeemScript); - } - } - } - } - - const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); - - int nHashType = SIGHASH_ALL; - if (params.size() > 3 && params[3].type() != null_type) - { - static map mapSigHashValues = - boost::assign::map_list_of - (string("ALL"), int(SIGHASH_ALL)) - (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) - (string("NONE"), int(SIGHASH_NONE)) - (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) - (string("SINGLE"), int(SIGHASH_SINGLE)) - (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) - ; - string strHashType = params[3].get_str(); - if (mapSigHashValues.count(strHashType)) - nHashType = mapSigHashValues[strHashType]; - else - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); - } - - bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); - - // Sign what we can: - for (unsigned int i = 0; i < mergedTx.vin.size(); i++) - { - CTxIn& txin = mergedTx.vin[i]; - CCoins coins; - if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) - { - fComplete = false; - continue; - } - const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey; - - txin.scriptSig.clear(); - // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mergedTx.vout.size())) - SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); - - // ... and merge in other signatures: - BOOST_FOREACH(const CTransaction& txv, txVariants) - { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); - } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) - fComplete = false; - } - - Object result; - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << mergedTx; - result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end()))); - result.push_back(Pair("complete", fComplete)); - - return result; -} - -Value sendrawtransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1) - throw runtime_error( - "sendrawtransaction \n" - "Submits raw transaction (serialized, hex-encoded) to local node and network."); - - // parse hex string from parameter - vector txData(ParseHexV(params[0], "parameter")); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - CTransaction tx; - - // deserialize binary data stream - try { - ssData >> tx; - } - catch (std::exception &e) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - } - uint256 hashTx = tx.GetHash(); - - bool fHave = false; - CCoinsViewCache &view = *pcoinsTip; - CCoins existingCoins; - { - fHave = view.GetCoins(hashTx, existingCoins); - if (!fHave) { - // push to local node - CValidationState state; - if (!tx.AcceptToMemoryPool(state, true, false)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state - } - } - if (fHave) { - if (existingCoins.nHeight < 1000000000) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain"); - // Not in block, but already in the memory pool; will drop - // through to re-relay it. - } else { - SyncWithWallets(hashTx, tx, NULL, true); - } - RelayTransaction(tx, hashTx); - - return hashTx.GetHex(); -} diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp deleted file mode 100644 index 57f54893..00000000 --- a/src/rpcwallet.cpp +++ /dev/null @@ -1,1579 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 The Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING - -#include - -#include "wallet.h" -#include "walletdb.h" -#include "bitcoinrpc.h" -#include "init.h" -#include "base58.h" - -using namespace std; -using namespace boost; -using namespace boost::assign; -using namespace json_spirit; - -int64 nWalletUnlockTime; -static CCriticalSection cs_nWalletUnlockTime; - -std::string HelpRequiringPassphrase() -{ - return pwalletMain->IsCrypted() - ? "\nrequires wallet passphrase to be set with walletpassphrase first" - : ""; -} - -void EnsureWalletIsUnlocked() -{ - if (pwalletMain->IsLocked()) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); -} - -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) -{ - int confirms = wtx.GetDepthInMainChain(); - entry.push_back(Pair("confirmations", confirms)); - if (wtx.IsCoinBase()) - entry.push_back(Pair("generated", true)); - if (confirms) - { - entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); - entry.push_back(Pair("blockindex", wtx.nIndex)); - entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); - } - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); - entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); - entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived)); - BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); -} - -string AccountFromValue(const Value& value) -{ - string strAccount = value.get_str(); - if (strAccount == "*") - throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); - return strAccount; -} - -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info."); - - proxyType proxy; - GetProxy(NET_IPV4, proxy); - - Object obj; - obj.push_back(Pair("version", FormatFullVersion())); - obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply))); - obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); - obj.push_back(Pair("testnet", fTestNet)); - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize())); - obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - if (pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000)); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - - -Value getnewaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewaddress [account]\n" - "Returns a new Primecoin address for receiving payments. " - "If [account] is specified (recommended), it is added to the address book " - "so payments received with the address will be credited to [account]."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - if (!pwalletMain->IsLocked()) - pwalletMain->TopUpKeyPool(); - - // Generate a new key that is added to wallet - CPubKey newKey; - if (!pwalletMain->GetKeyFromPool(newKey, false)) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - CKeyID keyID = newKey.GetID(); - - pwalletMain->SetAddressBookName(keyID, strAccount); - - return CBitcoinAddress(keyID).ToString(); -} - - -CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - bool bKeyUsed = false; - - // Check if the current key has been used - if (account.vchPubKey.IsValid()) - { - CScript scriptPubKey; - scriptPubKey.SetDestination(account.vchPubKey.GetID()); - for (map::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); - ++it) - { - const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - bKeyUsed = true; - } - } - - // Generate a new key - if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) - { - if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false)) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - - pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount); - walletdb.WriteAccount(strAccount, account); - } - - return CBitcoinAddress(account.vchPubKey.GetID()); -} - -Value getaccountaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccountaddress \n" - "Returns the current Primecoin address for receiving payments to this account."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount = AccountFromValue(params[0]); - - Value ret; - - ret = GetAccountAddress(strAccount).ToString(); - - return ret; -} - - - -Value setaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setaccount \n" - "Sets the account associated with the given address."); - - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Primecoin address"); - - - string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); - - // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwalletMain->mapAddressBook.count(address.Get())) - { - string strOldAccount = pwalletMain->mapAddressBook[address.Get()]; - if (address == GetAccountAddress(strOldAccount)) - GetAccountAddress(strOldAccount, true); - } - - pwalletMain->SetAddressBookName(address.Get(), strAccount); - - return Value::null; -} - - -Value getaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccount \n" - "Returns the account associated with the given address."); - - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Primecoin address"); - - string strAccount; - map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; - return strAccount; -} - - -Value getaddressesbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaddressesbyaccount \n" - "Returns the list of addresses for the given account."); - - string strAccount = AccountFromValue(params[0]); - - // Find all addresses that have the given account - Array ret; - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) - { - const CBitcoinAddress& address = item.first; - const string& strName = item.second; - if (strName == strAccount) - ret.push_back(address.ToString()); - } - return ret; -} - -Value sendtoaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001" - + HelpRequiringPassphrase()); - - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Primecoin address"); - - // Amount - int64 nAmount = AmountFromValue(params[1]); - if (nAmount < MIN_TXOUT_AMOUNT) - throw JSONRPCError(-101, "Send amount too small"); - - // Wallet comments - CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) - wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["to"] = params[3].get_str(); - - if (pwalletMain->IsLocked()) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); - - string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); - if (strError != "") - throw JSONRPCError(RPC_WALLET_ERROR, strError); - - return wtx.GetHash().GetHex(); -} - -Value listaddressgroupings(const Array& params, bool fHelp) -{ - if (fHelp) - throw runtime_error( - "listaddressgroupings\n" - "Lists groups of addresses which have had their common ownership\n" - "made public by common use as inputs or as the resulting change\n" - "in past transactions"); - - Array jsonGroupings; - map balances = pwalletMain->GetAddressBalances(); - BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) - { - Array jsonGrouping; - BOOST_FOREACH(CTxDestination address, grouping) - { - Array addressInfo; - addressInfo.push_back(CBitcoinAddress(address).ToString()); - addressInfo.push_back(ValueFromAmount(balances[address])); - { - LOCK(pwalletMain->cs_wallet); - if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) - addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second); - } - jsonGrouping.push_back(addressInfo); - } - jsonGroupings.push_back(jsonGrouping); - } - return jsonGroupings; -} - -Value signmessage(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 2) - throw runtime_error( - "signmessage \n" - "Sign a message with the private key of an address"); - - EnsureWalletIsUnlocked(); - - string strAddress = params[0].get_str(); - string strMessage = params[1].get_str(); - - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); - - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); - - CKey key; - if (!pwalletMain->GetKey(keyID, key)) - throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); - - CHashWriter ss(SER_GETHASH, 0); - ss << strMessageMagic; - ss << strMessage; - - vector vchSig; - if (!key.SignCompact(ss.GetHash(), vchSig)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); - - return EncodeBase64(&vchSig[0], vchSig.size()); -} - -Value verifymessage(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 3) - throw runtime_error( - "verifymessage \n" - "Verify a signed message"); - - string strAddress = params[0].get_str(); - string strSign = params[1].get_str(); - string strMessage = params[2].get_str(); - - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); - - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); - - bool fInvalid = false; - vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); - - if (fInvalid) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - - CHashWriter ss(SER_GETHASH, 0); - ss << strMessageMagic; - ss << strMessage; - - CKey key; - if (!key.SetCompactSignature(ss.GetHash(), vchSig)) - return false; - - return (key.GetPubKey().GetID() == keyID); -} - - -Value getreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaddress [minconf=1]\n" - "Returns the total amount received by in transactions with at least [minconf] confirmations."); - - // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); - CScript scriptPubKey; - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Primecoin address"); - scriptPubKey.SetDestination(address.Get()); - if (!IsMine(*pwalletMain,scriptPubKey)) - return (double)0.0; - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Tally - int64 nAmount = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - - return ValueFromAmount(nAmount); -} - - -void GetAccountAddresses(string strAccount, set& setAddress) -{ - BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook) - { - const CTxDestination& address = item.first; - const string& strName = item.second; - if (strName == strAccount) - setAddress.insert(address); - } -} - -Value getreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaccount [minconf=1]\n" - "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Get the set of pub keys assigned to account - string strAccount = AccountFromValue(params[0]); - set setAddress; - GetAccountAddresses(strAccount, setAddress); - - // Tally - int64 nAmount = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return (double)nAmount / (double)COIN; -} - - -int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) -{ - int64 nBalance = 0; - - // Tally wallet transactions - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance -= nSent + nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - - return nBalance; -} - -int64 GetAccountBalance(const string& strAccount, int nMinDepth) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth); -} - - -Value getbalance(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "getbalance [account] [minconf=1]\n" - "If [account] is not specified, returns the server's total available balance.\n" - "If [account] is specified, returns the balance in the account."); - - if (params.size() == 0) - return ValueFromAmount(pwalletMain->GetBalance()); - - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - if (params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' 0 should return the same number - int64 nBalance = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsConfirmed()) - continue; - - int64 allFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount); - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) - nBalance += r.second; - } - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent) - nBalance -= r.second; - nBalance -= allFee; - } - return ValueFromAmount(nBalance); - } - - string strAccount = AccountFromValue(params[0]); - - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - - return ValueFromAmount(nBalance); -} - - -Value movecmd(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error( - "move [minconf=1] [comment]\n" - "Move from one account in your wallet to another."); - - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - int64 nAmount = AmountFromValue(params[2]); - if (params.size() > 3) - // unused parameter, used to be nMinDepth, keep type-checking it though - (void)params[3].get_int(); - string strComment; - if (params.size() > 4) - strComment = params[4].get_str(); - - CWalletDB walletdb(pwalletMain->strWalletFile); - if (!walletdb.TxnBegin()) - throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - - int64 nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - walletdb.WriteAccountingEntry(debit); - - // Credit - CAccountingEntry credit; - credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - walletdb.WriteAccountingEntry(credit); - - if (!walletdb.TxnCommit()) - throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - - return true; -} - - -Value sendfrom(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 6) - throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001" - + HelpRequiringPassphrase()); - - string strAccount = AccountFromValue(params[0]); - CBitcoinAddress address(params[1].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Primecoin address"); - int64 nAmount = AmountFromValue(params[2]); - if (nAmount < MIN_TXOUT_AMOUNT) - throw JSONRPCError(-101, "Send amount too small"); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); - - EnsureWalletIsUnlocked(); - - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (nAmount > nBalance) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - - // Send - string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); - if (strError != "") - throw JSONRPCError(RPC_WALLET_ERROR, strError); - - return wtx.GetHash().GetHex(); -} - - -Value sendmany(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendmany {address:amount,...} [minconf=1] [comment]\n" - "amounts are double-precision floating point numbers" - + HelpRequiringPassphrase()); - - string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); - int nMinDepth = 1; - if (params.size() > 2) - nMinDepth = params[2].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["comment"] = params[3].get_str(); - - set setAddress; - vector > vecSend; - - int64 totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) - { - CBitcoinAddress address(s.name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Primecoin address: ")+s.name_); - - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); - setAddress.insert(address); - - CScript scriptPubKey; - scriptPubKey.SetDestination(address.Get()); - int64 nAmount = AmountFromValue(s.value_); - if (nAmount < MIN_TXOUT_AMOUNT) - throw JSONRPCError(-101, "Send amount too small"); - totalAmount += nAmount; - - vecSend.push_back(make_pair(scriptPubKey, nAmount)); - } - - EnsureWalletIsUnlocked(); - - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (totalAmount > nBalance) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - - // Send - CReserveKey keyChange(pwalletMain); - int64 nFeeRequired = 0; - string strFailReason; - bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); - if (!fCreated) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); - if (!pwalletMain->CommitTransaction(wtx, keyChange)) - throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); - - return wtx.GetHash().GetHex(); -} - -// -// Used by addmultisigaddress / createmultisig: -// -static CScript _createmultisig(const Array& params) -{ - int nRequired = params[0].get_int(); - const Array& keys = params[1].get_array(); - - // Gather public keys - if (nRequired < 1) - throw runtime_error("a multisignature address must require at least one key to redeem"); - if ((int)keys.size() < nRequired) - throw runtime_error( - strprintf("not enough keys supplied " - "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired)); - std::vector pubkeys; - pubkeys.resize(keys.size()); - for (unsigned int i = 0; i < keys.size(); i++) - { - const std::string& ks = keys[i].get_str(); - - // Case 1: Bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (address.IsValid()) - { - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw runtime_error( - strprintf("%s does not refer to a key",ks.c_str())); - CPubKey vchPubKey; - if (!pwalletMain->GetPubKey(keyID, vchPubKey)) - throw runtime_error( - strprintf("no full public key for address %s",ks.c_str())); - if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) - throw runtime_error(" Invalid public key: "+ks); - } - - // Case 2: hex public key - else if (IsHex(ks)) - { - CPubKey vchPubKey(ParseHex(ks)); - if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) - throw runtime_error(" Invalid public key: "+ks); - } - else - { - throw runtime_error(" Invalid public key: "+ks); - } - } - CScript result; - result.SetMultisig(nRequired, pubkeys); - return result; -} - -Value addmultisigaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - { - string msg = "addmultisigaddress <'[\"key\",\"key\"]'> [account]\n" - "Add a nrequired-to-sign multisignature address to the wallet\"\n" - "each key is a Primecoin address or hex-encoded public key\n" - "If [account] is specified, assign address to [account]."; - throw runtime_error(msg); - } - - string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); - - // Construct using pay-to-script-hash: - CScript inner = _createmultisig(params); - CScriptID innerID = inner.GetID(); - pwalletMain->AddCScript(inner); - - pwalletMain->SetAddressBookName(innerID, strAccount); - return CBitcoinAddress(innerID).ToString(); -} - -Value createmultisig(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 2) - { - string msg = "createmultisig <'[\"key\",\"key\"]'>\n" - "Creates a multi-signature address and returns a json object\n" - "with keys:\n" - "address : primecoin address\n" - "redeemScript : hex-encoded redemption script"; - throw runtime_error(msg); - } - - // Construct using pay-to-script-hash: - CScript inner = _createmultisig(params); - CScriptID innerID = inner.GetID(); - CBitcoinAddress address(innerID); - - Object result; - result.push_back(Pair("address", address.ToString())); - result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); - - return result; -} - - -struct tallyitem -{ - int64 nAmount; - int nConf; - tallyitem() - { - nAmount = 0; - nConf = std::numeric_limits::max(); - } -}; - -Value ListReceived(const Array& params, bool fByAccounts) -{ - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - // Whether to include empty accounts - bool fIncludeEmpty = false; - if (params.size() > 1) - fIncludeEmpty = params[1].get_bool(); - - // Tally - map mapTally; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - CTxDestination address; - if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address)) - continue; - - tallyitem& item = mapTally[address]; - item.nAmount += txout.nValue; - item.nConf = min(item.nConf, nDepth); - } - } - - // Reply - Array ret; - map mapAccountTally; - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) - { - const CBitcoinAddress& address = item.first; - const string& strAccount = item.second; - map::iterator it = mapTally.find(address); - if (it == mapTally.end() && !fIncludeEmpty) - continue; - - int64 nAmount = 0; - int nConf = std::numeric_limits::max(); - if (it != mapTally.end()) - { - nAmount = (*it).second.nAmount; - nConf = (*it).second.nConf; - } - - if (fByAccounts) - { - tallyitem& item = mapAccountTally[strAccount]; - item.nAmount += nAmount; - item.nConf = min(item.nConf, nConf); - } - else - { - Object obj; - obj.push_back(Pair("address", address.ToString())); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); - ret.push_back(obj); - } - } - - if (fByAccounts) - { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) - { - int64 nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; - Object obj; - obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); - ret.push_back(obj); - } - } - - return ret; -} - -Value listreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaddress [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include addresses that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"address\" : receiving address\n" - " \"account\" : the account of the receiving address\n" - " \"amount\" : total amount received by the address\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, false); -} - -Value listreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaccount [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include accounts that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"account\" : the account of the receiving addresses\n" - " \"amount\" : total amount received by addresses with this account\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, true); -} - -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) -{ - int64 nFee; - string strSentAccount; - list > listReceived; - list > listSent; - - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); - - bool fAllAccounts = (strAccount == string("*")); - - // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) - { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) - { - Object entry; - entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString())); - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.second))); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - - // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) - { - string account; - if (pwalletMain->mapAddressBook.count(r.first)) - account = pwalletMain->mapAddressBook[r.first]; - if (fAllAccounts || (account == strAccount)) - { - Object entry; - entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString())); - if (wtx.IsCoinBase()) - { - if (wtx.GetDepthInMainChain() < 1) - entry.push_back(Pair("category", "orphan")); - else if (wtx.GetBlocksToMaturity() > 0) - entry.push_back(Pair("category", "immature")); - else - entry.push_back(Pair("category", "generate")); - } - else - entry.push_back(Pair("category", "receive")); - entry.push_back(Pair("amount", ValueFromAmount(r.second))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - } -} - -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) -{ - bool fAllAccounts = (strAccount == string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - Object entry; - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); - ret.push_back(entry); - } -} - -Value listtransactions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 3) - throw runtime_error( - "listtransactions [account] [count=10] [from=0]\n" - "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); - - string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); - int nCount = 10; - if (params.size() > 1) - nCount = params[1].get_int(); - int nFrom = 0; - if (params.size() > 2) - nFrom = params[2].get_int(); - - if (nCount < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); - if (nFrom < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); - - Array ret; - - std::list acentries; - CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); - - // iterate backwards until we have nCount items to return: - for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) - AcentryToJSON(*pacentry, strAccount, ret); - - if ((int)ret.size() >= (nCount+nFrom)) break; - } - // ret is newest to oldest - - if (nFrom > (int)ret.size()) - nFrom = ret.size(); - if ((nFrom + nCount) > (int)ret.size()) - nCount = ret.size() - nFrom; - Array::iterator first = ret.begin(); - std::advance(first, nFrom); - Array::iterator last = ret.begin(); - std::advance(last, nFrom+nCount); - - if (last != ret.end()) ret.erase(last, ret.end()); - if (first != ret.begin()) ret.erase(ret.begin(), first); - - std::reverse(ret.begin(), ret.end()); // Return oldest to newest - - return ret; -} - -Value listaccounts(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "listaccounts [minconf=1]\n" - "Returns Object that has account names as keys, account balances as values."); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - map mapAccountBalances; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) { - if (IsMine(*pwalletMain, entry.first)) // This address belongs to me - mapAccountBalances[entry.second] = 0; - } - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - int64 nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); - mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) - if (pwalletMain->mapAddressBook.count(r.first)) - mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; - else - mapAccountBalances[""] += r.second; - } - } - - list acentries; - CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); - BOOST_FOREACH(const CAccountingEntry& entry, acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); - } - return ret; -} - -Value listsinceblock(const Array& params, bool fHelp) -{ - if (fHelp) - throw runtime_error( - "listsinceblock [blockhash] [target-confirmations]\n" - "Get all transactions in blocks since block [blockhash], or all transactions if omitted"); - - CBlockIndex *pindex = NULL; - int target_confirms = 1; - - if (params.size() > 0) - { - uint256 blockId = 0; - - blockId.SetHex(params[0].get_str()); - pindex = CBlockLocator(blockId).GetBlockIndex(); - } - - if (params.size() > 1) - { - target_confirms = params[1].get_int(); - - if (target_confirms < 1) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); - } - - int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; - - Array transactions; - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) - { - CWalletTx tx = (*it).second; - - if (depth == -1 || tx.GetDepthInMainChain() < depth) - ListTransactions(tx, "*", 0, true, transactions); - } - - uint256 lastblock; - - if (target_confirms == 1) - { - lastblock = hashBestChain; - } - else - { - int target_height = pindexBest->nHeight + 1 - target_confirms; - - CBlockIndex *block; - for (block = pindexBest; - block && block->nHeight > target_height; - block = block->pprev) { } - - lastblock = block ? block->GetBlockHash() : 0; - } - - Object ret; - ret.push_back(Pair("transactions", transactions)); - ret.push_back(Pair("lastblock", lastblock.GetHex())); - - return ret; -} - -Value gettransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "gettransaction \n" - "Get detailed information about in-wallet transaction "); - - uint256 hash; - hash.SetHex(params[0].get_str()); - - Object entry; - if (!pwalletMain->mapWallet.count(hash)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); - - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); - - WalletTxToJSON(wtx, entry); - - Array details; - ListTransactions(wtx, "*", 0, false, details); - entry.push_back(Pair("details", details)); - - return entry; -} - - -Value backupwallet(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "backupwallet \n" - "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); - - string strDest = params[0].get_str(); - if (!BackupWallet(*pwalletMain, strDest)) - throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); - - return Value::null; -} - - -Value keypoolrefill(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 0) - throw runtime_error( - "keypoolrefill\n" - "Fills the keypool." - + HelpRequiringPassphrase()); - - EnsureWalletIsUnlocked(); - - pwalletMain->TopUpKeyPool(); - - if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100)) - throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); - - return Value::null; -} - - -void ThreadTopUpKeyPool(void* parg) -{ - // Make this thread recognisable as the key-topping-up thread - RenameThread("primecoin-key-top"); - - pwalletMain->TopUpKeyPool(); -} - -void ThreadCleanWalletPassphrase(void* parg) -{ - // Make this thread recognisable as the wallet relocking thread - RenameThread("primecoin-lock-wa"); - - int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000; - - ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); - - if (nWalletUnlockTime == 0) - { - nWalletUnlockTime = nMyWakeTime; - - do - { - if (nWalletUnlockTime==0) - break; - int64 nToSleep = nWalletUnlockTime - GetTimeMillis(); - if (nToSleep <= 0) - break; - - LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - MilliSleep(nToSleep); - ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); - - } while(1); - - if (nWalletUnlockTime) - { - nWalletUnlockTime = 0; - pwalletMain->Lock(); - } - } - else - { - if (nWalletUnlockTime < nMyWakeTime) - nWalletUnlockTime = nMyWakeTime; - } - - LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - - delete (int64*)parg; -} - -Value walletpassphrase(const Array& params, bool fHelp) -{ - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) - throw runtime_error( - "walletpassphrase \n" - "Stores the wallet decryption key in memory for seconds."); - if (fHelp) - return true; - if (!pwalletMain->IsCrypted()) - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); - - if (!pwalletMain->IsLocked()) - throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); - - // Note that the walletpassphrase is stored in params[0] which is not mlock()ed - SecureString strWalletPass; - strWalletPass.reserve(100); - // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make params[0] mlock()'d to begin with. - strWalletPass = params[0].get_str().c_str(); - - if (strWalletPass.length() > 0) - { - if (!pwalletMain->Unlock(strWalletPass)) - throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - } - else - throw runtime_error( - "walletpassphrase \n" - "Stores the wallet decryption key in memory for seconds."); - - NewThread(ThreadTopUpKeyPool, NULL); - int64* pnSleepTime = new int64(params[1].get_int64()); - NewThread(ThreadCleanWalletPassphrase, pnSleepTime); - - return Value::null; -} - - -Value walletpassphrasechange(const Array& params, bool fHelp) -{ - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) - throw runtime_error( - "walletpassphrasechange \n" - "Changes the wallet passphrase from to ."); - if (fHelp) - return true; - if (!pwalletMain->IsCrypted()) - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); - - // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) - // Alternately, find a way to make params[0] mlock()'d to begin with. - SecureString strOldWalletPass; - strOldWalletPass.reserve(100); - strOldWalletPass = params[0].get_str().c_str(); - - SecureString strNewWalletPass; - strNewWalletPass.reserve(100); - strNewWalletPass = params[1].get_str().c_str(); - - if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) - throw runtime_error( - "walletpassphrasechange \n" - "Changes the wallet passphrase from to ."); - - if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) - throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - - return Value::null; -} - - -Value walletlock(const Array& params, bool fHelp) -{ - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) - throw runtime_error( - "walletlock\n" - "Removes the wallet encryption key from memory, locking the wallet.\n" - "After calling this method, you will need to call walletpassphrase again\n" - "before being able to call any methods which require the wallet to be unlocked."); - if (fHelp) - return true; - if (!pwalletMain->IsCrypted()) - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); - - { - LOCK(cs_nWalletUnlockTime); - pwalletMain->Lock(); - nWalletUnlockTime = 0; - } - - return Value::null; -} - - -Value encryptwallet(const Array& params, bool fHelp) -{ - if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) - throw runtime_error( - "encryptwallet \n" - "Encrypts the wallet with ."); - if (fHelp) - return true; - if (pwalletMain->IsCrypted()) - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); - - // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make params[0] mlock()'d to begin with. - SecureString strWalletPass; - strWalletPass.reserve(100); - strWalletPass = params[0].get_str().c_str(); - - if (strWalletPass.length() < 1) - throw runtime_error( - "encryptwallet \n" - "Encrypts the wallet with ."); - - if (!pwalletMain->EncryptWallet(strWalletPass)) - throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); - - // BDB seems to have a bad habit of writing old data into - // slack space in .dat files; that is bad if the old data is - // unencrypted private keys. So: - StartShutdown(); - return "wallet encrypted; Primecoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; -} - -class DescribeAddressVisitor : public boost::static_visitor -{ -public: - Object operator()(const CNoDestination &dest) const { return Object(); } - - Object operator()(const CKeyID &keyID) const { - Object obj; - CPubKey vchPubKey; - pwalletMain->GetPubKey(keyID, vchPubKey); - obj.push_back(Pair("isscript", false)); - obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw()))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); - return obj; - } - - Object operator()(const CScriptID &scriptID) const { - Object obj; - obj.push_back(Pair("isscript", true)); - CScript subscript; - pwalletMain->GetCScript(scriptID, subscript); - std::vector addresses; - txnouttype whichType; - int nRequired; - ExtractDestinations(subscript, whichType, addresses, nRequired); - obj.push_back(Pair("script", GetTxnOutputType(whichType))); - Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - obj.push_back(Pair("addresses", a)); - if (whichType == TX_MULTISIG) - obj.push_back(Pair("sigsrequired", nRequired)); - return obj; - } -}; - -Value validateaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "validateaddress \n" - "Return information about ."); - - CBitcoinAddress address(params[0].get_str()); - bool isValid = address.IsValid(); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - CTxDestination dest = address.Get(); - string currentAddress = address.ToString(); - ret.push_back(Pair("address", currentAddress)); - bool fMine = IsMine(*pwalletMain, dest); - ret.push_back(Pair("ismine", fMine)); - if (fMine) { - Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); - ret.insert(ret.end(), detail.begin(), detail.end()); - } - if (pwalletMain->mapAddressBook.count(dest)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest])); - } - return ret; -} - -Value lockunspent(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "lockunspent unlock? [array-of-Objects]\n" - "Updates list of temporarily unspendable outputs."); - - if (params.size() == 1) - RPCTypeCheck(params, list_of(bool_type)); - else - RPCTypeCheck(params, list_of(bool_type)(array_type)); - - bool fUnlock = params[0].get_bool(); - - if (params.size() == 1) { - if (fUnlock) - pwalletMain->UnlockAllCoins(); - return true; - } - - Array outputs = params[1].get_array(); - BOOST_FOREACH(Value& output, outputs) - { - if (output.type() != obj_type) - throw JSONRPCError(-8, "Invalid parameter, expected object"); - const Object& o = output.get_obj(); - - RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type)); - - string txid = find_value(o, "txid").get_str(); - if (!IsHex(txid)) - throw JSONRPCError(-8, "Invalid parameter, expected hex txid"); - - int nOutput = find_value(o, "vout").get_int(); - if (nOutput < 0) - throw JSONRPCError(-8, "Invalid parameter, vout must be positive"); - - COutPoint outpt(uint256(txid), nOutput); - - if (fUnlock) - pwalletMain->UnlockCoin(outpt); - else - pwalletMain->LockCoin(outpt); - } - - return true; -} - -Value listlockunspent(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 0) - throw runtime_error( - "listlockunspent\n" - "Returns list of temporarily unspendable outputs."); - - vector vOutpts; - pwalletMain->ListLockedCoins(vOutpts); - - Array ret; - - BOOST_FOREACH(COutPoint &outpt, vOutpts) { - Object o; - - o.push_back(Pair("txid", outpt.hash.GetHex())); - o.push_back(Pair("vout", (int)outpt.n)); - ret.push_back(o); - } - - return ret; -} - diff --git a/src/script.cpp b/src/script.cpp deleted file mode 100644 index cf6331fd..00000000 --- a/src/script.cpp +++ /dev/null @@ -1,1906 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include -#include - -#include "script.h" -#include "keystore.h" -#include "bignum.h" -#include "key.h" -#include "main.h" -#include "sync.h" -#include "util.h" - -using namespace std; -using namespace boost; - -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); - - - -typedef vector valtype; -static const valtype vchFalse(0); -static const valtype vchZero(0); -static const valtype vchTrue(1, 1); -static const CBigNum bnZero(0); -static const CBigNum bnOne(1); -static const CBigNum bnFalse(0); -static const CBigNum bnTrue(1); -static const size_t nMaxNumSize = 4; - - -CBigNum CastToBigNum(const valtype& vch) -{ - if (vch.size() > nMaxNumSize) - throw runtime_error("CastToBigNum() : overflow"); - // Get rid of extra leading zeros - return CBigNum(CBigNum(vch).getvch()); -} - -bool CastToBool(const valtype& vch) -{ - for (unsigned int i = 0; i < vch.size(); i++) - { - if (vch[i] != 0) - { - // Can be negative zero - if (i == vch.size()-1 && vch[i] == 0x80) - return false; - return true; - } - } - return false; -} - - - -// -// Script is a stack machine (like Forth) that evaluates a predicate -// returning a bool indicating valid or not. There are no loops. -// -#define stacktop(i) (stack.at(stack.size()+(i))) -#define altstacktop(i) (altstack.at(altstack.size()+(i))) -static inline void popstack(vector& stack) -{ - if (stack.empty()) - throw runtime_error("popstack() : stack empty"); - stack.pop_back(); -} - - -const char* GetTxnOutputType(txnouttype t) -{ - switch (t) - { - case TX_NONSTANDARD: return "nonstandard"; - case TX_PUBKEY: return "pubkey"; - case TX_PUBKEYHASH: return "pubkeyhash"; - case TX_SCRIPTHASH: return "scripthash"; - case TX_MULTISIG: return "multisig"; - } - return NULL; -} - - -const char* GetOpName(opcodetype opcode) -{ - switch (opcode) - { - // push value - case OP_0 : return "0"; - case OP_PUSHDATA1 : return "OP_PUSHDATA1"; - case OP_PUSHDATA2 : return "OP_PUSHDATA2"; - case OP_PUSHDATA4 : return "OP_PUSHDATA4"; - case OP_1NEGATE : return "-1"; - case OP_RESERVED : return "OP_RESERVED"; - case OP_1 : return "1"; - case OP_2 : return "2"; - case OP_3 : return "3"; - case OP_4 : return "4"; - case OP_5 : return "5"; - case OP_6 : return "6"; - case OP_7 : return "7"; - case OP_8 : return "8"; - case OP_9 : return "9"; - case OP_10 : return "10"; - case OP_11 : return "11"; - case OP_12 : return "12"; - case OP_13 : return "13"; - case OP_14 : return "14"; - case OP_15 : return "15"; - case OP_16 : return "16"; - - // control - case OP_NOP : return "OP_NOP"; - case OP_VER : return "OP_VER"; - case OP_IF : return "OP_IF"; - case OP_NOTIF : return "OP_NOTIF"; - case OP_VERIF : return "OP_VERIF"; - case OP_VERNOTIF : return "OP_VERNOTIF"; - case OP_ELSE : return "OP_ELSE"; - case OP_ENDIF : return "OP_ENDIF"; - case OP_VERIFY : return "OP_VERIFY"; - case OP_RETURN : return "OP_RETURN"; - - // stack ops - case OP_TOALTSTACK : return "OP_TOALTSTACK"; - case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; - case OP_2DROP : return "OP_2DROP"; - case OP_2DUP : return "OP_2DUP"; - case OP_3DUP : return "OP_3DUP"; - case OP_2OVER : return "OP_2OVER"; - case OP_2ROT : return "OP_2ROT"; - case OP_2SWAP : return "OP_2SWAP"; - case OP_IFDUP : return "OP_IFDUP"; - case OP_DEPTH : return "OP_DEPTH"; - case OP_DROP : return "OP_DROP"; - case OP_DUP : return "OP_DUP"; - case OP_NIP : return "OP_NIP"; - case OP_OVER : return "OP_OVER"; - case OP_PICK : return "OP_PICK"; - case OP_ROLL : return "OP_ROLL"; - case OP_ROT : return "OP_ROT"; - case OP_SWAP : return "OP_SWAP"; - case OP_TUCK : return "OP_TUCK"; - - // splice ops - case OP_CAT : return "OP_CAT"; - case OP_SUBSTR : return "OP_SUBSTR"; - case OP_LEFT : return "OP_LEFT"; - case OP_RIGHT : return "OP_RIGHT"; - case OP_SIZE : return "OP_SIZE"; - - // bit logic - case OP_INVERT : return "OP_INVERT"; - case OP_AND : return "OP_AND"; - case OP_OR : return "OP_OR"; - case OP_XOR : return "OP_XOR"; - case OP_EQUAL : return "OP_EQUAL"; - case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; - case OP_RESERVED1 : return "OP_RESERVED1"; - case OP_RESERVED2 : return "OP_RESERVED2"; - - // numeric - case OP_1ADD : return "OP_1ADD"; - case OP_1SUB : return "OP_1SUB"; - case OP_2MUL : return "OP_2MUL"; - case OP_2DIV : return "OP_2DIV"; - case OP_NEGATE : return "OP_NEGATE"; - case OP_ABS : return "OP_ABS"; - case OP_NOT : return "OP_NOT"; - case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; - case OP_ADD : return "OP_ADD"; - case OP_SUB : return "OP_SUB"; - case OP_MUL : return "OP_MUL"; - case OP_DIV : return "OP_DIV"; - case OP_MOD : return "OP_MOD"; - case OP_LSHIFT : return "OP_LSHIFT"; - case OP_RSHIFT : return "OP_RSHIFT"; - case OP_BOOLAND : return "OP_BOOLAND"; - case OP_BOOLOR : return "OP_BOOLOR"; - case OP_NUMEQUAL : return "OP_NUMEQUAL"; - case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; - case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; - case OP_LESSTHAN : return "OP_LESSTHAN"; - case OP_GREATERTHAN : return "OP_GREATERTHAN"; - case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; - case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; - case OP_MIN : return "OP_MIN"; - case OP_MAX : return "OP_MAX"; - case OP_WITHIN : return "OP_WITHIN"; - - // crypto - case OP_RIPEMD160 : return "OP_RIPEMD160"; - case OP_SHA1 : return "OP_SHA1"; - case OP_SHA256 : return "OP_SHA256"; - case OP_HASH160 : return "OP_HASH160"; - case OP_HASH256 : return "OP_HASH256"; - case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; - case OP_CHECKSIG : return "OP_CHECKSIG"; - case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; - case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; - case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - - // expanson - case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; - case OP_NOP3 : return "OP_NOP3"; - case OP_NOP4 : return "OP_NOP4"; - case OP_NOP5 : return "OP_NOP5"; - case OP_NOP6 : return "OP_NOP6"; - case OP_NOP7 : return "OP_NOP7"; - case OP_NOP8 : return "OP_NOP8"; - case OP_NOP9 : return "OP_NOP9"; - case OP_NOP10 : return "OP_NOP10"; - - - - // template matching params - case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; - case OP_PUBKEY : return "OP_PUBKEY"; - - case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; - default: - return "OP_UNKNOWN"; - } -} - -bool IsCanonicalPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() < 33) - return error("Non-canonical public key: too short"); - if (vchPubKey[0] == 0x04) { - if (vchPubKey.size() != 65) - return error("Non-canonical public key: invalid length for uncompressed key"); - } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { - if (vchPubKey.size() != 33) - return error("Non-canonical public key: invalid length for compressed key"); - } else { - return error("Non-canonical public key: compressed nor uncompressed"); - } - return true; -} - -bool IsCanonicalSignature(const valtype &vchSig) { - // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 - // A canonical signature exists of: <30> <02> <02> - // Where R and S are not negative (their first byte has its highest bit not set), and not - // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, - // in which case a single 0 byte is necessary and even required). - if (vchSig.size() < 9) - return error("Non-canonical signature: too short"); - if (vchSig.size() > 73) - return error("Non-canonical signature: too long"); - unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); - if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) - return error("Non-canonical signature: unknown hashtype byte"); - if (vchSig[0] != 0x30) - return error("Non-canonical signature: wrong type"); - if (vchSig[1] != vchSig.size()-3) - return error("Non-canonical signature: wrong length marker"); - unsigned int nLenR = vchSig[3]; - if (5 + nLenR >= vchSig.size()) - return error("Non-canonical signature: S length misplaced"); - unsigned int nLenS = vchSig[5+nLenR]; - if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) - return error("Non-canonical signature: R+S length mismatch"); - - const unsigned char *R = &vchSig[4]; - if (R[-2] != 0x02) - return error("Non-canonical signature: R value type mismatch"); - if (nLenR == 0) - return error("Non-canonical signature: R length is zero"); - if (R[0] & 0x80) - return error("Non-canonical signature: R value negative"); - if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) - return error("Non-canonical signature: R value excessively padded"); - - const unsigned char *S = &vchSig[6+nLenR]; - if (S[-2] != 0x02) - return error("Non-canonical signature: S value type mismatch"); - if (nLenS == 0) - return error("Non-canonical signature: S length is zero"); - if (S[0] & 0x80) - return error("Non-canonical signature: S value negative"); - if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) - return error("Non-canonical signature: S value excessively padded"); - - return true; -} - -bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) -{ - CAutoBN_CTX pctx; - CScript::const_iterator pc = script.begin(); - CScript::const_iterator pend = script.end(); - CScript::const_iterator pbegincodehash = script.begin(); - opcodetype opcode; - valtype vchPushValue; - vector vfExec; - vector altstack; - if (script.size() > 10000) - return false; - int nOpCount = 0; - bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; - - try - { - while (pc < pend) - { - bool fExec = !count(vfExec.begin(), vfExec.end(), false); - - // - // Read instruction - // - if (!script.GetOp(pc, opcode, vchPushValue)) - return false; - if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) - return false; - if (opcode > OP_16 && ++nOpCount > 201) - return false; - - if (opcode == OP_CAT || - opcode == OP_SUBSTR || - opcode == OP_LEFT || - opcode == OP_RIGHT || - opcode == OP_INVERT || - opcode == OP_AND || - opcode == OP_OR || - opcode == OP_XOR || - opcode == OP_2MUL || - opcode == OP_2DIV || - opcode == OP_MUL || - opcode == OP_DIV || - opcode == OP_MOD || - opcode == OP_LSHIFT || - opcode == OP_RSHIFT) - return false; // Disabled opcodes. - - if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) - stack.push_back(vchPushValue); - else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) - switch (opcode) - { - // - // Push value - // - case OP_1NEGATE: - case OP_1: - case OP_2: - case OP_3: - case OP_4: - case OP_5: - case OP_6: - case OP_7: - case OP_8: - case OP_9: - case OP_10: - case OP_11: - case OP_12: - case OP_13: - case OP_14: - case OP_15: - case OP_16: - { - // ( -- value) - CBigNum bn((int)opcode - (int)(OP_1 - 1)); - stack.push_back(bn.getvch()); - } - break; - - - // - // Control - // - case OP_NOP: - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: - case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: - break; - - case OP_IF: - case OP_NOTIF: - { - // if [statements] [else [statements]] endif - bool fValue = false; - if (fExec) - { - if (stack.size() < 1) - return false; - valtype& vch = stacktop(-1); - fValue = CastToBool(vch); - if (opcode == OP_NOTIF) - fValue = !fValue; - popstack(stack); - } - vfExec.push_back(fValue); - } - break; - - case OP_ELSE: - { - if (vfExec.empty()) - return false; - vfExec.back() = !vfExec.back(); - } - break; - - case OP_ENDIF: - { - if (vfExec.empty()) - return false; - vfExec.pop_back(); - } - break; - - case OP_VERIFY: - { - // (true -- ) or - // (false -- false) and return - if (stack.size() < 1) - return false; - bool fValue = CastToBool(stacktop(-1)); - if (fValue) - popstack(stack); - else - return false; - } - break; - - case OP_RETURN: - { - return false; - } - break; - - - // - // Stack ops - // - case OP_TOALTSTACK: - { - if (stack.size() < 1) - return false; - altstack.push_back(stacktop(-1)); - popstack(stack); - } - break; - - case OP_FROMALTSTACK: - { - if (altstack.size() < 1) - return false; - stack.push_back(altstacktop(-1)); - popstack(altstack); - } - break; - - case OP_2DROP: - { - // (x1 x2 -- ) - if (stack.size() < 2) - return false; - popstack(stack); - popstack(stack); - } - break; - - case OP_2DUP: - { - // (x1 x2 -- x1 x2 x1 x2) - if (stack.size() < 2) - return false; - valtype vch1 = stacktop(-2); - valtype vch2 = stacktop(-1); - stack.push_back(vch1); - stack.push_back(vch2); - } - break; - - case OP_3DUP: - { - // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) - if (stack.size() < 3) - return false; - valtype vch1 = stacktop(-3); - valtype vch2 = stacktop(-2); - valtype vch3 = stacktop(-1); - stack.push_back(vch1); - stack.push_back(vch2); - stack.push_back(vch3); - } - break; - - case OP_2OVER: - { - // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) - if (stack.size() < 4) - return false; - valtype vch1 = stacktop(-4); - valtype vch2 = stacktop(-3); - stack.push_back(vch1); - stack.push_back(vch2); - } - break; - - case OP_2ROT: - { - // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) - if (stack.size() < 6) - return false; - valtype vch1 = stacktop(-6); - valtype vch2 = stacktop(-5); - stack.erase(stack.end()-6, stack.end()-4); - stack.push_back(vch1); - stack.push_back(vch2); - } - break; - - case OP_2SWAP: - { - // (x1 x2 x3 x4 -- x3 x4 x1 x2) - if (stack.size() < 4) - return false; - swap(stacktop(-4), stacktop(-2)); - swap(stacktop(-3), stacktop(-1)); - } - break; - - case OP_IFDUP: - { - // (x - 0 | x x) - if (stack.size() < 1) - return false; - valtype vch = stacktop(-1); - if (CastToBool(vch)) - stack.push_back(vch); - } - break; - - case OP_DEPTH: - { - // -- stacksize - CBigNum bn(stack.size()); - stack.push_back(bn.getvch()); - } - break; - - case OP_DROP: - { - // (x -- ) - if (stack.size() < 1) - return false; - popstack(stack); - } - break; - - case OP_DUP: - { - // (x -- x x) - if (stack.size() < 1) - return false; - valtype vch = stacktop(-1); - stack.push_back(vch); - } - break; - - case OP_NIP: - { - // (x1 x2 -- x2) - if (stack.size() < 2) - return false; - stack.erase(stack.end() - 2); - } - break; - - case OP_OVER: - { - // (x1 x2 -- x1 x2 x1) - if (stack.size() < 2) - return false; - valtype vch = stacktop(-2); - stack.push_back(vch); - } - break; - - case OP_PICK: - case OP_ROLL: - { - // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) - // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) - if (stack.size() < 2) - return false; - int n = CastToBigNum(stacktop(-1)).getint(); - popstack(stack); - if (n < 0 || n >= (int)stack.size()) - return false; - valtype vch = stacktop(-n-1); - if (opcode == OP_ROLL) - stack.erase(stack.end()-n-1); - stack.push_back(vch); - } - break; - - case OP_ROT: - { - // (x1 x2 x3 -- x2 x3 x1) - // x2 x1 x3 after first swap - // x2 x3 x1 after second swap - if (stack.size() < 3) - return false; - swap(stacktop(-3), stacktop(-2)); - swap(stacktop(-2), stacktop(-1)); - } - break; - - case OP_SWAP: - { - // (x1 x2 -- x2 x1) - if (stack.size() < 2) - return false; - swap(stacktop(-2), stacktop(-1)); - } - break; - - case OP_TUCK: - { - // (x1 x2 -- x2 x1 x2) - if (stack.size() < 2) - return false; - valtype vch = stacktop(-1); - stack.insert(stack.end()-2, vch); - } - break; - - - case OP_SIZE: - { - // (in -- in size) - if (stack.size() < 1) - return false; - CBigNum bn(stacktop(-1).size()); - stack.push_back(bn.getvch()); - } - break; - - - // - // Bitwise logic - // - case OP_EQUAL: - case OP_EQUALVERIFY: - //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL - { - // (x1 x2 - bool) - if (stack.size() < 2) - return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); - bool fEqual = (vch1 == vch2); - // OP_NOTEQUAL is disabled because it would be too easy to say - // something like n != 1 and have some wiseguy pass in 1 with extra - // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) - //if (opcode == OP_NOTEQUAL) - // fEqual = !fEqual; - popstack(stack); - popstack(stack); - stack.push_back(fEqual ? vchTrue : vchFalse); - if (opcode == OP_EQUALVERIFY) - { - if (fEqual) - popstack(stack); - else - return false; - } - } - break; - - - // - // Numeric - // - case OP_1ADD: - case OP_1SUB: - case OP_NEGATE: - case OP_ABS: - case OP_NOT: - case OP_0NOTEQUAL: - { - // (in -- out) - if (stack.size() < 1) - return false; - CBigNum bn = CastToBigNum(stacktop(-1)); - switch (opcode) - { - case OP_1ADD: bn += bnOne; break; - case OP_1SUB: bn -= bnOne; break; - case OP_NEGATE: bn = -bn; break; - case OP_ABS: if (bn < bnZero) bn = -bn; break; - case OP_NOT: bn = (bn == bnZero); break; - case OP_0NOTEQUAL: bn = (bn != bnZero); break; - default: assert(!"invalid opcode"); break; - } - popstack(stack); - stack.push_back(bn.getvch()); - } - break; - - case OP_ADD: - case OP_SUB: - case OP_BOOLAND: - case OP_BOOLOR: - case OP_NUMEQUAL: - case OP_NUMEQUALVERIFY: - case OP_NUMNOTEQUAL: - case OP_LESSTHAN: - case OP_GREATERTHAN: - case OP_LESSTHANOREQUAL: - case OP_GREATERTHANOREQUAL: - case OP_MIN: - case OP_MAX: - { - // (x1 x2 -- out) - if (stack.size() < 2) - return false; - CBigNum bn1 = CastToBigNum(stacktop(-2)); - CBigNum bn2 = CastToBigNum(stacktop(-1)); - CBigNum bn; - switch (opcode) - { - case OP_ADD: - bn = bn1 + bn2; - break; - - case OP_SUB: - bn = bn1 - bn2; - break; - - case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; - case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; - case OP_NUMEQUAL: bn = (bn1 == bn2); break; - case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; - case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; - case OP_LESSTHAN: bn = (bn1 < bn2); break; - case OP_GREATERTHAN: bn = (bn1 > bn2); break; - case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; - case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; - case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; - case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; - default: assert(!"invalid opcode"); break; - } - popstack(stack); - popstack(stack); - stack.push_back(bn.getvch()); - - if (opcode == OP_NUMEQUALVERIFY) - { - if (CastToBool(stacktop(-1))) - popstack(stack); - else - return false; - } - } - break; - - case OP_WITHIN: - { - // (x min max -- out) - if (stack.size() < 3) - return false; - CBigNum bn1 = CastToBigNum(stacktop(-3)); - CBigNum bn2 = CastToBigNum(stacktop(-2)); - CBigNum bn3 = CastToBigNum(stacktop(-1)); - bool fValue = (bn2 <= bn1 && bn1 < bn3); - popstack(stack); - popstack(stack); - popstack(stack); - stack.push_back(fValue ? vchTrue : vchFalse); - } - break; - - - // - // Crypto - // - case OP_RIPEMD160: - case OP_SHA1: - case OP_SHA256: - case OP_HASH160: - case OP_HASH256: - { - // (in -- hash) - if (stack.size() < 1) - return false; - valtype& vch = stacktop(-1); - valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); - if (opcode == OP_RIPEMD160) - RIPEMD160(&vch[0], vch.size(), &vchHash[0]); - else if (opcode == OP_SHA1) - SHA1(&vch[0], vch.size(), &vchHash[0]); - else if (opcode == OP_SHA256) - SHA256(&vch[0], vch.size(), &vchHash[0]); - else if (opcode == OP_HASH160) - { - uint160 hash160 = Hash160(vch); - memcpy(&vchHash[0], &hash160, sizeof(hash160)); - } - else if (opcode == OP_HASH256) - { - uint256 hash = Hash(vch.begin(), vch.end()); - memcpy(&vchHash[0], &hash, sizeof(hash)); - } - popstack(stack); - stack.push_back(vchHash); - } - break; - - case OP_CODESEPARATOR: - { - // Hash starts after the code separator - pbegincodehash = pc; - } - break; - - case OP_CHECKSIG: - case OP_CHECKSIGVERIFY: - { - // (sig pubkey -- bool) - if (stack.size() < 2) - return false; - - valtype& vchSig = stacktop(-2); - valtype& vchPubKey = stacktop(-1); - - ////// debug print - //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); - //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); - - // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); - - // Drop the signature, since there's no way for a signature to sign itself - scriptCode.FindAndDelete(CScript(vchSig)); - - bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); - if (fSuccess) - fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); - - popstack(stack); - popstack(stack); - stack.push_back(fSuccess ? vchTrue : vchFalse); - if (opcode == OP_CHECKSIGVERIFY) - { - if (fSuccess) - popstack(stack); - else - return false; - } - } - break; - - case OP_CHECKMULTISIG: - case OP_CHECKMULTISIGVERIFY: - { - // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) - - int i = 1; - if ((int)stack.size() < i) - return false; - - int nKeysCount = CastToBigNum(stacktop(-i)).getint(); - if (nKeysCount < 0 || nKeysCount > 20) - return false; - nOpCount += nKeysCount; - if (nOpCount > 201) - return false; - int ikey = ++i; - i += nKeysCount; - if ((int)stack.size() < i) - return false; - - int nSigsCount = CastToBigNum(stacktop(-i)).getint(); - if (nSigsCount < 0 || nSigsCount > nKeysCount) - return false; - int isig = ++i; - i += nSigsCount; - if ((int)stack.size() < i) - return false; - - // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); - - // Drop the signatures, since there's no way for a signature to sign itself - for (int k = 0; k < nSigsCount; k++) - { - valtype& vchSig = stacktop(-isig-k); - scriptCode.FindAndDelete(CScript(vchSig)); - } - - bool fSuccess = true; - while (fSuccess && nSigsCount > 0) - { - valtype& vchSig = stacktop(-isig); - valtype& vchPubKey = stacktop(-ikey); - - // Check signature - bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); - if (fOk) - fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); - - if (fOk) { - isig++; - nSigsCount--; - } - ikey++; - nKeysCount--; - - // If there are more signatures left than keys left, - // then too many signatures have failed - if (nSigsCount > nKeysCount) - fSuccess = false; - } - - while (i-- > 0) - popstack(stack); - stack.push_back(fSuccess ? vchTrue : vchFalse); - - if (opcode == OP_CHECKMULTISIGVERIFY) - { - if (fSuccess) - popstack(stack); - else - return false; - } - } - break; - - default: - return false; - } - - // Size limits - if (stack.size() + altstack.size() > 1000) - return false; - } - } - catch (...) - { - return false; - } - - - if (!vfExec.empty()) - return false; - - return true; -} - - - - - - - - - -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - if (nIn >= txTo.vin.size()) - { - printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); - return 1; - } - CTransaction txTmp(txTo); - - // In case concatenating two scripts ends up with two codeseparators, - // or an extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); - - // Blank out other inputs' signatures - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - txTmp.vin[i].scriptSig = CScript(); - txTmp.vin[nIn].scriptSig = scriptCode; - - // Blank out some of the outputs - if ((nHashType & 0x1f) == SIGHASH_NONE) - { - // Wildcard payee - txTmp.vout.clear(); - - // Let the others update at will - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; - } - else if ((nHashType & 0x1f) == SIGHASH_SINGLE) - { - // Only lock-in the txout payee at same index as txin - unsigned int nOut = nIn; - if (nOut >= txTmp.vout.size()) - { - printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); - return 1; - } - txTmp.vout.resize(nOut+1); - for (unsigned int i = 0; i < nOut; i++) - txTmp.vout[i].SetNull(); - - // Let the others update at will - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; - } - - // Blank out other inputs completely, not recommended for open transactions - if (nHashType & SIGHASH_ANYONECANPAY) - { - txTmp.vin[0] = txTmp.vin[nIn]; - txTmp.vin.resize(1); - } - - // Serialize and hash - CHashWriter ss(SER_GETHASH, 0); - ss << txTmp << nHashType; - return ss.GetHash(); -} - - -// Valid signature cache, to avoid doing expensive ECDSA signature checking -// twice for every transaction (once when accepted into memory pool, and -// again when accepted into the block chain) - -class CSignatureCache -{ -private: - // sigdata_type is (signature hash, signature, public key): - typedef boost::tuple, std::vector > sigdata_type; - std::set< sigdata_type> setValid; - boost::shared_mutex cs_sigcache; - -public: - bool - Get(uint256 hash, const std::vector& vchSig, const std::vector& pubKey) - { - boost::shared_lock lock(cs_sigcache); - - sigdata_type k(hash, vchSig, pubKey); - std::set::iterator mi = setValid.find(k); - if (mi != setValid.end()) - return true; - return false; - } - - void Set(uint256 hash, const std::vector& vchSig, const std::vector& pubKey) - { - // DoS prevention: limit cache size to less than 10MB - // (~200 bytes per cache entry times 50,000 entries) - // Since there are a maximum of 20,000 signature operations per block - // 50,000 is a reasonable default. - int64 nMaxCacheSize = GetArg("-maxsigcachesize", 50000); - if (nMaxCacheSize <= 0) return; - - boost::unique_lock lock(cs_sigcache); - - while (static_cast(setValid.size()) > nMaxCacheSize) - { - // Evict a random entry. Random because that helps - // foil would-be DoS attackers who might try to pre-generate - // and re-use a set of valid signatures just-slightly-greater - // than our cache size. - uint256 randomHash = GetRandHash(); - std::vector unused; - std::set::iterator it = - setValid.lower_bound(sigdata_type(randomHash, unused, unused)); - if (it == setValid.end()) - it = setValid.begin(); - setValid.erase(*it); - } - - sigdata_type k(hash, vchSig, pubKey); - setValid.insert(k); - } -}; - -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, - const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) -{ - static CSignatureCache signatureCache; - - // Hash type is one byte tacked on to the end of the signature - if (vchSig.empty()) - return false; - if (nHashType == 0) - nHashType = vchSig.back(); - else if (nHashType != vchSig.back()) - return false; - vchSig.pop_back(); - - uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); - - if (signatureCache.Get(sighash, vchSig, vchPubKey)) - return true; - - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - - if (!key.Verify(sighash, vchSig)) - return false; - - if (!(flags & SCRIPT_VERIFY_NOCACHE)) - signatureCache.Set(sighash, vchSig, vchPubKey); - - return true; -} - - - - - - - - - -// -// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. -// -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutionsRet) -{ - // Templates - static map mTemplates; - if (mTemplates.empty()) - { - // Standard tx, sender provides pubkey, receiver adds signature - mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); - - // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); - - // Sender provides N pubkeys, receivers provides M signatures - mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); - } - - // Shortcut for pay-to-script-hash, which are more constrained than the other types: - // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL - if (scriptPubKey.IsPayToScriptHash()) - { - typeRet = TX_SCRIPTHASH; - vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); - vSolutionsRet.push_back(hashBytes); - return true; - } - - // Scan templates - const CScript& script1 = scriptPubKey; - BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) - { - const CScript& script2 = tplate.second; - vSolutionsRet.clear(); - - opcodetype opcode1, opcode2; - vector vch1, vch2; - - // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - loop - { - if (pc1 == script1.end() && pc2 == script2.end()) - { - // Found a match - typeRet = tplate.first; - if (typeRet == TX_MULTISIG) - { - // Additional checks for TX_MULTISIG: - unsigned char m = vSolutionsRet.front()[0]; - unsigned char n = vSolutionsRet.back()[0]; - if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) - return false; - } - return true; - } - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - - // Template matching opcodes: - if (opcode2 == OP_PUBKEYS) - { - while (vch1.size() >= 33 && vch1.size() <= 120) - { - vSolutionsRet.push_back(vch1); - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - } - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - // Normal situation is to fall through - // to other if/else statements - } - - if (opcode2 == OP_PUBKEY) - { - if (vch1.size() < 33 || vch1.size() > 120) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_PUBKEYHASH) - { - if (vch1.size() != sizeof(uint160)) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_SMALLINTEGER) - { // Single-byte small integer pushed onto vSolutions - if (opcode1 == OP_0 || - (opcode1 >= OP_1 && opcode1 <= OP_16)) - { - char n = (char)CScript::DecodeOP_N(opcode1); - vSolutionsRet.push_back(valtype(1, n)); - } - else - break; - } - else if (opcode1 != opcode2 || vch1 != vch2) - { - // Others must match exactly - break; - } - } - } - - vSolutionsRet.clear(); - typeRet = TX_NONSTANDARD; - return false; -} - - -bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) -{ - CKey key; - if (!keystore.GetKey(address, key)) - return false; - - vector vchSig; - if (!key.Sign(hash, vchSig)) - return false; - vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig; - - return true; -} - -bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) -{ - int nSigned = 0; - int nRequired = multisigdata.front()[0]; - for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) - { - const valtype& pubkey = multisigdata[i]; - CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) - ++nSigned; - } - return nSigned==nRequired; -} - -// -// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. -// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), -// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. -// Returns false if scriptPubKey could not be completely satisfied. -// -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, - CScript& scriptSigRet, txnouttype& whichTypeRet) -{ - scriptSigRet.clear(); - - vector vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) - return false; - - CKeyID keyID; - switch (whichTypeRet) - { - case TX_NONSTANDARD: - return false; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) - return false; - else - { - CPubKey vch; - keystore.GetPubKey(keyID, vch); - scriptSigRet << vch; - } - return true; - case TX_SCRIPTHASH: - return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); - - case TX_MULTISIG: - scriptSigRet << OP_0; // workaround CHECKMULTISIG bug - return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); - } - return false; -} - -int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions) -{ - switch (t) - { - case TX_NONSTANDARD: - return -1; - case TX_PUBKEY: - return 1; - case TX_PUBKEYHASH: - return 2; - case TX_MULTISIG: - if (vSolutions.size() < 1 || vSolutions[0].size() < 1) - return -1; - return vSolutions[0][0] + 1; - case TX_SCRIPTHASH: - return 1; // doesn't include args needed by the script - } - return -1; -} - -bool IsStandard(const CScript& scriptPubKey) -{ - vector vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_MULTISIG) - { - unsigned char m = vSolutions.front()[0]; - unsigned char n = vSolutions.back()[0]; - // Support up to x-of-3 multisig txns as standard - if (n < 1 || n > 3) - return false; - if (m < 1 || m > n) - return false; - } - - return whichType != TX_NONSTANDARD; -} - - -unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) -{ - unsigned int nResult = 0; - BOOST_FOREACH(const valtype& pubkey, pubkeys) - { - CKeyID keyID = CPubKey(pubkey).GetID(); - if (keystore.HaveKey(keyID)) - ++nResult; - } - return nResult; -} - - -class CKeyStoreIsMineVisitor : public boost::static_visitor -{ -private: - const CKeyStore *keystore; -public: - CKeyStoreIsMineVisitor(const CKeyStore *keystoreIn) : keystore(keystoreIn) { } - bool operator()(const CNoDestination &dest) const { return false; } - bool operator()(const CKeyID &keyID) const { return keystore->HaveKey(keyID); } - bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } -}; - -bool IsMine(const CKeyStore &keystore, const CTxDestination &dest) -{ - return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); -} - -bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) -{ - vector vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - CKeyID keyID; - switch (whichType) - { - case TX_NONSTANDARD: - return false; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return keystore.HaveKey(keyID); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - return keystore.HaveKey(keyID); - case TX_SCRIPTHASH: - { - CScript subscript; - if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript)) - return false; - return IsMine(keystore, subscript); - } - case TX_MULTISIG: - { - // Only consider transactions "mine" if we own ALL the - // keys involved. multi-signature transactions that are - // partially owned (somebody else has a key that can spend - // them) enable spend-out-from-under-you attacks, especially - // in shared-wallet situations. - vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - return HaveKeys(keys, keystore) == keys.size(); - } - } - return false; -} - -bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) -{ - vector vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_PUBKEY) - { - addressRet = CPubKey(vSolutions[0]).GetID(); - return true; - } - else if (whichType == TX_PUBKEYHASH) - { - addressRet = CKeyID(uint160(vSolutions[0])); - return true; - } - else if (whichType == TX_SCRIPTHASH) - { - addressRet = CScriptID(uint160(vSolutions[0])); - return true; - } - // Multisig txns have more than one address... - return false; -} - -bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) -{ - addressRet.clear(); - typeRet = TX_NONSTANDARD; - vector vSolutions; - if (!Solver(scriptPubKey, typeRet, vSolutions)) - return false; - - if (typeRet == TX_MULTISIG) - { - nRequiredRet = vSolutions.front()[0]; - for (unsigned int i = 1; i < vSolutions.size()-1; i++) - { - CTxDestination address = CPubKey(vSolutions[i]).GetID(); - addressRet.push_back(address); - } - } - else - { - nRequiredRet = 1; - CTxDestination address; - if (!ExtractDestination(scriptPubKey, address)) - return false; - addressRet.push_back(address); - } - - return true; -} - -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - unsigned int flags, int nHashType) -{ - vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) - return false; - if (flags & SCRIPT_VERIFY_P2SH) - stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType)) - return false; - if (stack.empty()) - return false; - - if (CastToBool(stack.back()) == false) - return false; - - // Additional validation for spend-to-script-hash transactions: - if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) - { - if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only - return false; // or validation fails - - // stackCopy cannot be empty here, because if it was the - // P2SH HASH <> EQUAL scriptPubKey would be evaluated with - // an empty stack and the EvalScript above would return false. - assert(!stackCopy.empty()); - - const valtype& pubKeySerialized = stackCopy.back(); - CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); - popstack(stackCopy); - - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType)) - return false; - if (stackCopy.empty()) - return false; - return CastToBool(stackCopy.back()); - } - - return true; -} - - -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - - // Leave out the signature from the hash, since a signature can't sign itself. - // The checksig op will also drop the signatures from its hash. - uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); - - txnouttype whichType; - if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) - return false; - - if (whichType == TX_SCRIPTHASH) - { - // Solver returns the subscript that need to be evaluated; - // the final scriptSig is the signatures from that - // and then the serialized subscript: - CScript subscript = txin.scriptSig; - - // Recompute txn hash using subscript in place of scriptPubKey: - uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); - - txnouttype subType; - bool fSolved = - Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; - // Append serialized subscript whether or not it is completely signed: - txin.scriptSig << static_cast(subscript); - if (!fSolved) return false; - } - - // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); -} - -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); -} - -static CScript PushAll(const vector& values) -{ - CScript result; - BOOST_FOREACH(const valtype& v, values) - result << v; - return result; -} - -static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const vector& vSolutions, - vector& sigs1, vector& sigs2) -{ - // Combine all the signatures we've got: - set allsigs; - BOOST_FOREACH(const valtype& v, sigs1) - { - if (!v.empty()) - allsigs.insert(v); - } - BOOST_FOREACH(const valtype& v, sigs2) - { - if (!v.empty()) - allsigs.insert(v); - } - - // Build a map of pubkey -> signature by matching sigs to pubkeys: - assert(vSolutions.size() > 1); - unsigned int nSigsRequired = vSolutions.front()[0]; - unsigned int nPubKeys = vSolutions.size()-2; - map sigs; - BOOST_FOREACH(const valtype& sig, allsigs) - { - for (unsigned int i = 0; i < nPubKeys; i++) - { - const valtype& pubkey = vSolutions[i+1]; - if (sigs.count(pubkey)) - continue; // Already got a sig for this pubkey - - if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) - { - sigs[pubkey] = sig; - break; - } - } - } - // Now build a merged CScript: - unsigned int nSigsHave = 0; - CScript result; result << OP_0; // pop-one-too-many workaround - for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) - { - if (sigs.count(vSolutions[i+1])) - { - result << sigs[vSolutions[i+1]]; - ++nSigsHave; - } - } - // Fill any missing with OP_0: - for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result << OP_0; - - return result; -} - -static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const txnouttype txType, const vector& vSolutions, - vector& sigs1, vector& sigs2) -{ - switch (txType) - { - case TX_NONSTANDARD: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.size() >= sigs2.size()) - return PushAll(sigs1); - return PushAll(sigs2); - case TX_PUBKEY: - case TX_PUBKEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.empty() || sigs1[0].empty()) - return PushAll(sigs2); - return PushAll(sigs1); - case TX_SCRIPTHASH: - if (sigs1.empty() || sigs1.back().empty()) - return PushAll(sigs2); - else if (sigs2.empty() || sigs2.back().empty()) - return PushAll(sigs1); - else - { - // Recur to combine: - valtype spk = sigs1.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - vector > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.pop_back(); - sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); - result << spk; - return result; - } - case TX_MULTISIG: - return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); - } - - return CScript(); -} - -CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const CScript& scriptSig1, const CScript& scriptSig2) -{ - txnouttype txType; - vector > vSolutions; - Solver(scriptPubKey, txType, vSolutions); - - vector stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); - vector stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); - - return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); -} - -unsigned int CScript::GetSigOpCount(bool fAccurate) const -{ - unsigned int n = 0; - const_iterator pc = begin(); - opcodetype lastOpcode = OP_INVALIDOPCODE; - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - break; - if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) - n++; - else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) - { - if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) - n += DecodeOP_N(lastOpcode); - else - n += 20; - } - lastOpcode = opcode; - } - return n; -} - -unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const -{ - if (!IsPayToScriptHash()) - return GetSigOpCount(true); - - // This is a pay-to-script-hash scriptPubKey; - // get the last item that the scriptSig - // pushes onto the stack: - const_iterator pc = scriptSig.begin(); - vector data; - while (pc < scriptSig.end()) - { - opcodetype opcode; - if (!scriptSig.GetOp(pc, opcode, data)) - return 0; - if (opcode > OP_16) - return 0; - } - - /// ... and return its opcount: - CScript subscript(data.begin(), data.end()); - return subscript.GetSigOpCount(true); -} - -bool CScript::IsPayToScriptHash() const -{ - // Extra-fast test for pay-to-script-hash CScripts: - return (this->size() == 23 && - this->at(0) == OP_HASH160 && - this->at(1) == 0x14 && - this->at(22) == OP_EQUAL); -} - -class CScriptVisitor : public boost::static_visitor -{ -private: - CScript *script; -public: - CScriptVisitor(CScript *scriptin) { script = scriptin; } - - bool operator()(const CNoDestination &dest) const { - script->clear(); - return false; - } - - bool operator()(const CKeyID &keyID) const { - script->clear(); - *script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; - return true; - } - - bool operator()(const CScriptID &scriptID) const { - script->clear(); - *script << OP_HASH160 << scriptID << OP_EQUAL; - return true; - } -}; - -void CScript::SetDestination(const CTxDestination& dest) -{ - boost::apply_visitor(CScriptVisitor(this), dest); -} - -void CScript::SetMultisig(int nRequired, const std::vector& keys) -{ - this->clear(); - - *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CKey& key, keys) - *this << key.GetPubKey(); - *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; -} - -bool CScriptCompressor::IsToKeyID(CKeyID &hash) const -{ - if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 - && script[2] == 20 && script[23] == OP_EQUALVERIFY - && script[24] == OP_CHECKSIG) { - memcpy(&hash, &script[3], 20); - return true; - } - return false; -} - -bool CScriptCompressor::IsToScriptID(CScriptID &hash) const -{ - if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 - && script[22] == OP_EQUAL) { - memcpy(&hash, &script[2], 20); - return true; - } - return false; -} - -bool CScriptCompressor::IsToPubKey(std::vector &pubkey) const -{ - if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG - && (script[1] == 0x02 || script[1] == 0x03)) { - pubkey.resize(33); - memcpy(&pubkey[0], &script[1], 33); - return true; - } - if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG - && script[1] == 0x04) { - pubkey.resize(65); - memcpy(&pubkey[0], &script[1], 65); - CKey key; - return (key.SetPubKey(CPubKey(pubkey))); // SetPubKey fails if this is not a valid public key, a case that would not be compressible - } - return false; -} - -bool CScriptCompressor::Compress(std::vector &out) const -{ - CKeyID keyID; - if (IsToKeyID(keyID)) { - out.resize(21); - out[0] = 0x00; - memcpy(&out[1], &keyID, 20); - return true; - } - CScriptID scriptID; - if (IsToScriptID(scriptID)) { - out.resize(21); - out[0] = 0x01; - memcpy(&out[1], &scriptID, 20); - return true; - } - std::vector pubkey; - if (IsToPubKey(pubkey)) { - out.resize(33); - memcpy(&out[1], &pubkey[1], 32); - if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { - out[0] = pubkey[0]; - return true; - } else if (pubkey[0] == 0x04) { - out[0] = 0x04 | (pubkey[64] & 0x01); - return true; - } - } - return false; -} - -unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const -{ - if (nSize == 0 || nSize == 1) - return 20; - if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5) - return 32; - return 0; -} - -bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector &in) -{ - switch(nSize) { - case 0x00: - script.resize(25); - script[0] = OP_DUP; - script[1] = OP_HASH160; - script[2] = 20; - memcpy(&script[3], &in[0], 20); - script[23] = OP_EQUALVERIFY; - script[24] = OP_CHECKSIG; - return true; - case 0x01: - script.resize(23); - script[0] = OP_HASH160; - script[1] = 20; - memcpy(&script[2], &in[0], 20); - script[22] = OP_EQUAL; - return true; - case 0x02: - case 0x03: - script.resize(35); - script[0] = 33; - script[1] = nSize; - memcpy(&script[2], &in[0], 32); - script[34] = OP_CHECKSIG; - return true; - case 0x04: - case 0x05: - std::vector vch(33, 0x00); - vch[0] = nSize - 2; - memcpy(&vch[1], &in[0], 32); - CKey key; - if (!key.SetPubKey(CPubKey(vch))) - return false; - key.SetCompressedPubKey(false); // Decompress public key - CPubKey pubkey = key.GetPubKey(); - script.resize(67); - script[0] = 65; - memcpy(&script[1], &pubkey.Raw()[0], 65); - script[66] = OP_CHECKSIG; - return true; - } - return false; -} diff --git a/src/script.h b/src/script.h deleted file mode 100644 index 4b29f627..00000000 --- a/src/script.h +++ /dev/null @@ -1,685 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef H_BITCOIN_SCRIPT -#define H_BITCOIN_SCRIPT - -#include -#include - -#include -#include - -#include "keystore.h" -#include "bignum.h" - -class CCoins; -class CTransaction; - -static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes - -/** Signature hash types/flags */ -enum -{ - SIGHASH_ALL = 1, - SIGHASH_NONE = 2, - SIGHASH_SINGLE = 3, - SIGHASH_ANYONECANPAY = 0x80, -}; - -/** Script verification flags */ -enum -{ - SCRIPT_VERIFY_NONE = 0, - SCRIPT_VERIFY_P2SH = (1U << 0), - SCRIPT_VERIFY_STRICTENC = (1U << 1), - SCRIPT_VERIFY_NOCACHE = (1U << 2), -}; - -enum txnouttype -{ - TX_NONSTANDARD, - // 'standard' transaction types: - TX_PUBKEY, - TX_PUBKEYHASH, - TX_SCRIPTHASH, - TX_MULTISIG, -}; - -class CNoDestination { -public: - friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } - friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } -}; - -/** A txout script template with a specific destination. It is either: - * * CNoDestination: no destination set - * * CKeyID: TX_PUBKEYHASH destination - * * CScriptID: TX_SCRIPTHASH destination - * A CTxDestination is the internal data type encoded in a CBitcoinAddress - */ -typedef boost::variant CTxDestination; - -const char* GetTxnOutputType(txnouttype t); - -/** Script opcodes */ -enum opcodetype -{ - // push value - OP_0 = 0x00, - OP_FALSE = OP_0, - OP_PUSHDATA1 = 0x4c, - OP_PUSHDATA2 = 0x4d, - OP_PUSHDATA4 = 0x4e, - OP_1NEGATE = 0x4f, - OP_RESERVED = 0x50, - OP_1 = 0x51, - OP_TRUE=OP_1, - OP_2 = 0x52, - OP_3 = 0x53, - OP_4 = 0x54, - OP_5 = 0x55, - OP_6 = 0x56, - OP_7 = 0x57, - OP_8 = 0x58, - OP_9 = 0x59, - OP_10 = 0x5a, - OP_11 = 0x5b, - OP_12 = 0x5c, - OP_13 = 0x5d, - OP_14 = 0x5e, - OP_15 = 0x5f, - OP_16 = 0x60, - - // control - OP_NOP = 0x61, - OP_VER = 0x62, - OP_IF = 0x63, - OP_NOTIF = 0x64, - OP_VERIF = 0x65, - OP_VERNOTIF = 0x66, - OP_ELSE = 0x67, - OP_ENDIF = 0x68, - OP_VERIFY = 0x69, - OP_RETURN = 0x6a, - - // stack ops - OP_TOALTSTACK = 0x6b, - OP_FROMALTSTACK = 0x6c, - OP_2DROP = 0x6d, - OP_2DUP = 0x6e, - OP_3DUP = 0x6f, - OP_2OVER = 0x70, - OP_2ROT = 0x71, - OP_2SWAP = 0x72, - OP_IFDUP = 0x73, - OP_DEPTH = 0x74, - OP_DROP = 0x75, - OP_DUP = 0x76, - OP_NIP = 0x77, - OP_OVER = 0x78, - OP_PICK = 0x79, - OP_ROLL = 0x7a, - OP_ROT = 0x7b, - OP_SWAP = 0x7c, - OP_TUCK = 0x7d, - - // splice ops - OP_CAT = 0x7e, - OP_SUBSTR = 0x7f, - OP_LEFT = 0x80, - OP_RIGHT = 0x81, - OP_SIZE = 0x82, - - // bit logic - OP_INVERT = 0x83, - OP_AND = 0x84, - OP_OR = 0x85, - OP_XOR = 0x86, - OP_EQUAL = 0x87, - OP_EQUALVERIFY = 0x88, - OP_RESERVED1 = 0x89, - OP_RESERVED2 = 0x8a, - - // numeric - OP_1ADD = 0x8b, - OP_1SUB = 0x8c, - OP_2MUL = 0x8d, - OP_2DIV = 0x8e, - OP_NEGATE = 0x8f, - OP_ABS = 0x90, - OP_NOT = 0x91, - OP_0NOTEQUAL = 0x92, - - OP_ADD = 0x93, - OP_SUB = 0x94, - OP_MUL = 0x95, - OP_DIV = 0x96, - OP_MOD = 0x97, - OP_LSHIFT = 0x98, - OP_RSHIFT = 0x99, - - OP_BOOLAND = 0x9a, - OP_BOOLOR = 0x9b, - OP_NUMEQUAL = 0x9c, - OP_NUMEQUALVERIFY = 0x9d, - OP_NUMNOTEQUAL = 0x9e, - OP_LESSTHAN = 0x9f, - OP_GREATERTHAN = 0xa0, - OP_LESSTHANOREQUAL = 0xa1, - OP_GREATERTHANOREQUAL = 0xa2, - OP_MIN = 0xa3, - OP_MAX = 0xa4, - - OP_WITHIN = 0xa5, - - // crypto - OP_RIPEMD160 = 0xa6, - OP_SHA1 = 0xa7, - OP_SHA256 = 0xa8, - OP_HASH160 = 0xa9, - OP_HASH256 = 0xaa, - OP_CODESEPARATOR = 0xab, - OP_CHECKSIG = 0xac, - OP_CHECKSIGVERIFY = 0xad, - OP_CHECKMULTISIG = 0xae, - OP_CHECKMULTISIGVERIFY = 0xaf, - - // expansion - OP_NOP1 = 0xb0, - OP_NOP2 = 0xb1, - OP_NOP3 = 0xb2, - OP_NOP4 = 0xb3, - OP_NOP5 = 0xb4, - OP_NOP6 = 0xb5, - OP_NOP7 = 0xb6, - OP_NOP8 = 0xb7, - OP_NOP9 = 0xb8, - OP_NOP10 = 0xb9, - - - - // template matching params - OP_SMALLINTEGER = 0xfa, - OP_PUBKEYS = 0xfb, - OP_PUBKEYHASH = 0xfd, - OP_PUBKEY = 0xfe, - - OP_INVALIDOPCODE = 0xff, -}; - -const char* GetOpName(opcodetype opcode); - - - -inline std::string ValueString(const std::vector& vch) -{ - if (vch.size() <= 4) - return strprintf("%d", CBigNum(vch).getint()); - else - return HexStr(vch); -} - -inline std::string StackString(const std::vector >& vStack) -{ - std::string str; - BOOST_FOREACH(const std::vector& vch, vStack) - { - if (!str.empty()) - str += " "; - str += ValueString(vch); - } - return str; -} - - - - - - - - -/** Serialized script, used inside transaction inputs and outputs */ -class CScript : public std::vector -{ -protected: - CScript& push_int64(int64 n) - { - if (n == -1 || (n >= 1 && n <= 16)) - { - push_back(n + (OP_1 - 1)); - } - else - { - CBigNum bn(n); - *this << bn.getvch(); - } - return *this; - } - - CScript& push_uint64(uint64 n) - { - if (n >= 1 && n <= 16) - { - push_back(n + (OP_1 - 1)); - } - else - { - CBigNum bn(n); - *this << bn.getvch(); - } - return *this; - } - -public: - CScript() { } - CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } - CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } -#ifndef _MSC_VER - CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } -#endif - - CScript& operator+=(const CScript& b) - { - insert(end(), b.begin(), b.end()); - return *this; - } - - friend CScript operator+(const CScript& a, const CScript& b) - { - CScript ret = a; - ret += b; - return ret; - } - - - //explicit CScript(char b) is not portable. Use 'signed char' or 'unsigned char'. - explicit CScript(signed char b) { operator<<(b); } - explicit CScript(short b) { operator<<(b); } - explicit CScript(int b) { operator<<(b); } - explicit CScript(long b) { operator<<(b); } - explicit CScript(int64 b) { operator<<(b); } - explicit CScript(unsigned char b) { operator<<(b); } - explicit CScript(unsigned int b) { operator<<(b); } - explicit CScript(unsigned short b) { operator<<(b); } - explicit CScript(unsigned long b) { operator<<(b); } - explicit CScript(uint64 b) { operator<<(b); } - - explicit CScript(opcodetype b) { operator<<(b); } - explicit CScript(const uint256& b) { operator<<(b); } - explicit CScript(const CBigNum& b) { operator<<(b); } - explicit CScript(const std::vector& b) { operator<<(b); } - - - //CScript& operator<<(char b) is not portable. Use 'signed char' or 'unsigned char'. - CScript& operator<<(signed char b) { return push_int64(b); } - CScript& operator<<(short b) { return push_int64(b); } - CScript& operator<<(int b) { return push_int64(b); } - CScript& operator<<(long b) { return push_int64(b); } - CScript& operator<<(int64 b) { return push_int64(b); } - CScript& operator<<(unsigned char b) { return push_uint64(b); } - CScript& operator<<(unsigned int b) { return push_uint64(b); } - CScript& operator<<(unsigned short b) { return push_uint64(b); } - CScript& operator<<(unsigned long b) { return push_uint64(b); } - CScript& operator<<(uint64 b) { return push_uint64(b); } - - CScript& operator<<(opcodetype opcode) - { - if (opcode < 0 || opcode > 0xff) - throw std::runtime_error("CScript::operator<<() : invalid opcode"); - insert(end(), (unsigned char)opcode); - return *this; - } - - CScript& operator<<(const uint160& b) - { - insert(end(), sizeof(b)); - insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); - return *this; - } - - CScript& operator<<(const uint256& b) - { - insert(end(), sizeof(b)); - insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); - return *this; - } - - CScript& operator<<(const CPubKey& key) - { - std::vector vchKey = key.Raw(); - return (*this) << vchKey; - } - - CScript& operator<<(const CBigNum& b) - { - *this << b.getvch(); - return *this; - } - - CScript& operator<<(const std::vector& b) - { - if (b.size() < OP_PUSHDATA1) - { - insert(end(), (unsigned char)b.size()); - } - else if (b.size() <= 0xff) - { - insert(end(), OP_PUSHDATA1); - insert(end(), (unsigned char)b.size()); - } - else if (b.size() <= 0xffff) - { - insert(end(), OP_PUSHDATA2); - unsigned short nSize = b.size(); - insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); - } - else - { - insert(end(), OP_PUSHDATA4); - unsigned int nSize = b.size(); - insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); - } - insert(end(), b.begin(), b.end()); - return *this; - } - - CScript& operator<<(const CScript& b) - { - // I'm not sure if this should push the script or concatenate scripts. - // If there's ever a use for pushing a script onto a script, delete this member fn - assert(!"Warning: Pushing a CScript onto a CScript with << is probably not intended, use + to concatenate!"); - return *this; - } - - - bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) - { - // Wrapper so it can be called with either iterator or const_iterator - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, &vchRet); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(iterator& pc, opcodetype& opcodeRet) - { - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, NULL); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) const - { - return GetOp2(pc, opcodeRet, &vchRet); - } - - bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const - { - return GetOp2(pc, opcodeRet, NULL); - } - - bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const - { - opcodeRet = OP_INVALIDOPCODE; - if (pvchRet) - pvchRet->clear(); - if (pc >= end()) - return false; - - // Read instruction - if (end() - pc < 1) - return false; - unsigned int opcode = *pc++; - - // Immediate operand - if (opcode <= OP_PUSHDATA4) - { - unsigned int nSize = 0; - if (opcode < OP_PUSHDATA1) - { - nSize = opcode; - } - else if (opcode == OP_PUSHDATA1) - { - if (end() - pc < 1) - return false; - nSize = *pc++; - } - else if (opcode == OP_PUSHDATA2) - { - if (end() - pc < 2) - return false; - nSize = 0; - memcpy(&nSize, &pc[0], 2); - pc += 2; - } - else if (opcode == OP_PUSHDATA4) - { - if (end() - pc < 4) - return false; - memcpy(&nSize, &pc[0], 4); - pc += 4; - } - if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) - return false; - if (pvchRet) - pvchRet->assign(pc, pc + nSize); - pc += nSize; - } - - opcodeRet = (opcodetype)opcode; - return true; - } - - // Encode/decode small integers: - static int DecodeOP_N(opcodetype opcode) - { - if (opcode == OP_0) - return 0; - assert(opcode >= OP_1 && opcode <= OP_16); - return (int)opcode - (int)(OP_1 - 1); - } - static opcodetype EncodeOP_N(int n) - { - assert(n >= 0 && n <= 16); - if (n == 0) - return OP_0; - return (opcodetype)(OP_1+n-1); - } - - int FindAndDelete(const CScript& b) - { - int nFound = 0; - if (b.empty()) - return nFound; - iterator pc = begin(); - opcodetype opcode; - do - { - while (end() - pc >= (long)b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) - { - erase(pc, pc + b.size()); - ++nFound; - } - } - while (GetOp(pc, opcode)); - return nFound; - } - int Find(opcodetype op) const - { - int nFound = 0; - opcodetype opcode; - for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) - if (opcode == op) - ++nFound; - return nFound; - } - - // Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs - // as 20 sigops. With pay-to-script-hash, that changed: - // CHECKMULTISIGs serialized in scriptSigs are - // counted more accurately, assuming they are of the form - // ... OP_N CHECKMULTISIG ... - unsigned int GetSigOpCount(bool fAccurate) const; - - // Accurately count sigOps, including sigOps in - // pay-to-script-hash transactions: - unsigned int GetSigOpCount(const CScript& scriptSig) const; - - bool IsPayToScriptHash() const; - - // Called by CTransaction::IsStandard - bool IsPushOnly() const - { - const_iterator pc = begin(); - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - return false; - if (opcode > OP_16) - return false; - } - return true; - } - - - void SetDestination(const CTxDestination& address); - void SetMultisig(int nRequired, const std::vector& keys); - - - void PrintHex() const - { - printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); - } - - std::string ToString() const - { - std::string str; - opcodetype opcode; - std::vector vch; - const_iterator pc = begin(); - while (pc < end()) - { - if (!str.empty()) - str += " "; - if (!GetOp(pc, opcode, vch)) - { - str += "[error]"; - return str; - } - if (0 <= opcode && opcode <= OP_PUSHDATA4) - str += ValueString(vch); - else - str += GetOpName(opcode); - } - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } - - CScriptID GetID() const - { - return CScriptID(Hash160(*this)); - } -}; - -/** Compact serializer for scripts. - * - * It detects common cases and encodes them much more efficiently. - * 3 special cases are defined: - * * Pay to pubkey hash (encoded as 21 bytes) - * * Pay to script hash (encoded as 21 bytes) - * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) - * - * Other scripts up to 121 bytes require 1 byte + script length. Above - * that, scripts up to 16505 bytes require 2 bytes + script length. - */ -class CScriptCompressor -{ -private: - // make this static for now (there are only 6 special scripts defined) - // this can potentially be extended together with a new nVersion for - // transactions, in which case this value becomes dependent on nVersion - // and nHeight of the enclosing transaction. - static const unsigned int nSpecialScripts = 6; - - CScript &script; -protected: - // These check for scripts for which a special case with a shorter encoding is defined. - // They are implemented separately from the CScript test, as these test for exact byte - // sequence correspondences, and are more strict. For example, IsToPubKey also verifies - // whether the public key is valid (as invalid ones cannot be represented in compressed - // form). - bool IsToKeyID(CKeyID &hash) const; - bool IsToScriptID(CScriptID &hash) const; - bool IsToPubKey(std::vector &pubkey) const; - - bool Compress(std::vector &out) const; - unsigned int GetSpecialSize(unsigned int nSize) const; - bool Decompress(unsigned int nSize, const std::vector &out); -public: - CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - std::vector compr; - if (Compress(compr)) - return compr.size(); - unsigned int nSize = script.size() + nSpecialScripts; - return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion); - } - - template - void Serialize(Stream &s, int nType, int nVersion) const { - std::vector compr; - if (Compress(compr)) { - s << CFlatData(&compr[0], &compr[compr.size()]); - return; - } - unsigned int nSize = script.size() + nSpecialScripts; - s << VARINT(nSize); - s << CFlatData(&script[0], &script[script.size()]); - } - - template - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nSize = 0; - s >> VARINT(nSize); - if (nSize < nSpecialScripts) { - std::vector vch(GetSpecialSize(nSize), 0x00); - s >> REF(CFlatData(&vch[0], &vch[vch.size()])); - Decompress(nSize, vch); - return; - } - nSize -= nSpecialScripts; - script.resize(nSize); - s >> REF(CFlatData(&script[0], &script[script.size()])); - } -}; - -bool IsCanonicalPubKey(const std::vector &vchPubKey); -bool IsCanonicalSignature(const std::vector &vchSig); - -bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); -int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); -bool IsStandard(const CScript& scriptPubKey); -bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); -bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); -bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); - -// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, -// combine them intelligently and return the result. -CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); - -#endif diff --git a/src/serialize.h b/src/serialize.h deleted file mode 100644 index e3d9939b..00000000 --- a/src/serialize.h +++ /dev/null @@ -1,1374 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_SERIALIZE_H -#define BITCOIN_SERIALIZE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "allocators.h" -#include "version.h" - -typedef long long int64; -typedef unsigned long long uint64; - -class CScript; -class CDataStream; -class CAutoFile; -static const unsigned int MAX_SIZE = 0x02000000; - -// Used to bypass the rule against non-const reference to temporary -// where it makes sense with wrappers such as CFlatData or CTxDB -template -inline T& REF(const T& val) -{ - return const_cast(val); -} - -///////////////////////////////////////////////////////////////// -// -// Templates for serializing to anything that looks like a stream, -// i.e. anything that supports .read(char*, int) and .write(char*, int) -// - -enum -{ - // primary actions - SER_NETWORK = (1 << 0), - SER_DISK = (1 << 1), - SER_GETHASH = (1 << 2), -}; - -#define IMPLEMENT_SERIALIZE(statements) \ - unsigned int GetSerializeSize(int nType, int nVersion) const \ - { \ - CSerActionGetSerializeSize ser_action; \ - const bool fGetSize = true; \ - const bool fWrite = false; \ - const bool fRead = false; \ - unsigned int nSerSize = 0; \ - ser_streamplaceholder s; \ - assert(fGetSize||fWrite||fRead); /* suppress warning */ \ - s.nType = nType; \ - s.nVersion = nVersion; \ - {statements} \ - return nSerSize; \ - } \ - template \ - void Serialize(Stream& s, int nType, int nVersion) const \ - { \ - CSerActionSerialize ser_action; \ - const bool fGetSize = false; \ - const bool fWrite = true; \ - const bool fRead = false; \ - unsigned int nSerSize = 0; \ - assert(fGetSize||fWrite||fRead); /* suppress warning */ \ - {statements} \ - } \ - template \ - void Unserialize(Stream& s, int nType, int nVersion) \ - { \ - CSerActionUnserialize ser_action; \ - const bool fGetSize = false; \ - const bool fWrite = false; \ - const bool fRead = true; \ - unsigned int nSerSize = 0; \ - assert(fGetSize||fWrite||fRead); /* suppress warning */ \ - {statements} \ - } - -#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) - - - - - - -// -// Basic types -// -#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) -#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) - -inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } - -template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } - -template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } - -inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } -template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } -template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } - - - - - - -// -// Compact size -// size < 253 -- 1 byte -// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) -// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) -// size > UINT_MAX -- 9 bytes (255 + 8 bytes) -// -inline unsigned int GetSizeOfCompactSize(uint64 nSize) -{ - if (nSize < 253) return sizeof(unsigned char); - else if (nSize <= std::numeric_limits::max()) return sizeof(unsigned char) + sizeof(unsigned short); - else if (nSize <= std::numeric_limits::max()) return sizeof(unsigned char) + sizeof(unsigned int); - else return sizeof(unsigned char) + sizeof(uint64); -} - -template -void WriteCompactSize(Stream& os, uint64 nSize) -{ - if (nSize < 253) - { - unsigned char chSize = nSize; - WRITEDATA(os, chSize); - } - else if (nSize <= std::numeric_limits::max()) - { - unsigned char chSize = 253; - unsigned short xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - else if (nSize <= std::numeric_limits::max()) - { - unsigned char chSize = 254; - unsigned int xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - else - { - unsigned char chSize = 255; - uint64 xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - return; -} - -template -uint64 ReadCompactSize(Stream& is) -{ - unsigned char chSize; - READDATA(is, chSize); - uint64 nSizeRet = 0; - if (chSize < 253) - { - nSizeRet = chSize; - } - else if (chSize == 253) - { - unsigned short xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - else if (chSize == 254) - { - unsigned int xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - else - { - uint64 xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - if (nSizeRet > (uint64)MAX_SIZE) - throw std::ios_base::failure("ReadCompactSize() : size too large"); - return nSizeRet; -} - -// Variable-length integers: bytes are a MSB base-128 encoding of the number. -// The high bit in each byte signifies whether another digit follows. To make -// the encoding is one-to-one, one is subtracted from all but the last digit. -// Thus, the byte sequence a[] with length len, where all but the last byte -// has bit 128 set, encodes the number: -// -// (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) -// -// Properties: -// * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) -// * Every integer has exactly one encoding -// * Encoding does not depend on size of original integer type -// * No redundancy: every (infinite) byte sequence corresponds to a list -// of encoded integers. -// -// 0: [0x00] 256: [0x81 0x00] -// 1: [0x01] 16383: [0xFE 0x7F] -// 127: [0x7F] 16384: [0xFF 0x00] -// 128: [0x80 0x00] 16511: [0x80 0xFF 0x7F] -// 255: [0x80 0x7F] 65535: [0x82 0xFD 0x7F] -// 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] - -template -inline unsigned int GetSizeOfVarInt(I n) -{ - int nRet = 0; - while(true) { - nRet++; - if (n <= 0x7F) - break; - n = (n >> 7) - 1; - } - return nRet; -} - -template -void WriteVarInt(Stream& os, I n) -{ - unsigned char tmp[(sizeof(n)*8+6)/7]; - int len=0; - while(true) { - tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00); - if (n <= 0x7F) - break; - n = (n >> 7) - 1; - len++; - } - do { - WRITEDATA(os, tmp[len]); - } while(len--); -} - -template -I ReadVarInt(Stream& is) -{ - I n = 0; - while(true) { - unsigned char chData; - READDATA(is, chData); - n = (n << 7) | (chData & 0x7F); - if (chData & 0x80) - n++; - else - return n; - } -} - -#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) -#define VARINT(obj) REF(WrapVarInt(REF(obj))) - -/** Wrapper for serializing arrays and POD. - */ -class CFlatData -{ -protected: - char* pbegin; - char* pend; -public: - CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } - char* begin() { return pbegin; } - const char* begin() const { return pbegin; } - char* end() { return pend; } - const char* end() const { return pend; } - - unsigned int GetSerializeSize(int, int=0) const - { - return pend - pbegin; - } - - template - void Serialize(Stream& s, int, int=0) const - { - s.write(pbegin, pend - pbegin); - } - - template - void Unserialize(Stream& s, int, int=0) - { - s.read(pbegin, pend - pbegin); - } -}; - -template -class CVarInt -{ -protected: - I &n; -public: - CVarInt(I& nIn) : n(nIn) { } - - unsigned int GetSerializeSize(int, int) const { - return GetSizeOfVarInt(n); - } - - template - void Serialize(Stream &s, int, int) const { - WriteVarInt(s, n); - } - - template - void Unserialize(Stream& s, int, int) { - n = ReadVarInt(s); - } -}; - -template -CVarInt WrapVarInt(I& n) { return CVarInt(n); } - -// -// Forward declarations -// - -// string -template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); -template void Serialize(Stream& os, const std::basic_string& str, int, int=0); -template void Unserialize(Stream& is, std::basic_string& str, int, int=0); - -// vector -template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); -template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion); -template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); -template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion); -template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); -template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion); - -// others derived from vector -extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion); -template void Serialize(Stream& os, const CScript& v, int nType, int nVersion); -template void Unserialize(Stream& is, CScript& v, int nType, int nVersion); - -// pair -template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion); -template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion); -template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion); - -// 3 tuple -template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion); -template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion); -template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion); - -// 4 tuple -template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion); -template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion); -template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion); - -// map -template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion); -template void Serialize(Stream& os, const std::map& m, int nType, int nVersion); -template void Unserialize(Stream& is, std::map& m, int nType, int nVersion); - -// set -template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion); -template void Serialize(Stream& os, const std::set& m, int nType, int nVersion); -template void Unserialize(Stream& is, std::set& m, int nType, int nVersion); - - - - - -// -// If none of the specialized versions above matched, default to calling member function. -// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. -// The compiler will only cast int to long if none of the other templates matched. -// Thanks to Boost serialization for this idea. -// -template -inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion) -{ - return a.GetSerializeSize((int)nType, nVersion); -} - -template -inline void Serialize(Stream& os, const T& a, long nType, int nVersion) -{ - a.Serialize(os, (int)nType, nVersion); -} - -template -inline void Unserialize(Stream& is, T& a, long nType, int nVersion) -{ - a.Unserialize(is, (int)nType, nVersion); -} - - - - - -// -// string -// -template -unsigned int GetSerializeSize(const std::basic_string& str, int, int) -{ - return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); -} - -template -void Serialize(Stream& os, const std::basic_string& str, int, int) -{ - WriteCompactSize(os, str.size()); - if (!str.empty()) - os.write((char*)&str[0], str.size() * sizeof(str[0])); -} - -template -void Unserialize(Stream& is, std::basic_string& str, int, int) -{ - unsigned int nSize = ReadCompactSize(is); - str.resize(nSize); - if (nSize != 0) - is.read((char*)&str[0], nSize * sizeof(str[0])); -} - - - -// -// vector -// -template -unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); -} - -template -unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - unsigned int nSize = GetSizeOfCompactSize(v.size()); - for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) - nSize += GetSerializeSize((*vi), nType, nVersion); - return nSize; -} - -template -inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) -{ - return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); -} - - -template -void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - WriteCompactSize(os, v.size()); - if (!v.empty()) - os.write((char*)&v[0], v.size() * sizeof(T)); -} - -template -void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - WriteCompactSize(os, v.size()); - for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) - ::Serialize(os, (*vi), nType, nVersion); -} - -template -inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) -{ - Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); -} - - -template -void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - // Limit size per read so bogus size value won't cause out of memory - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - while (i < nSize) - { - unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); - v.resize(i + blk); - is.read((char*)&v[i], blk * sizeof(T)); - i += blk; - } -} - -template -void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - unsigned int nMid = 0; - while (nMid < nSize) - { - nMid += 5000000 / sizeof(T); - if (nMid > nSize) - nMid = nSize; - v.resize(nMid); - for (; i < nMid; i++) - Unserialize(is, v[i], nType, nVersion); - } -} - -template -inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) -{ - Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); -} - - - -// -// others derived from vector -// -inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) -{ - return GetSerializeSize((const std::vector&)v, nType, nVersion); -} - -template -void Serialize(Stream& os, const CScript& v, int nType, int nVersion) -{ - Serialize(os, (const std::vector&)v, nType, nVersion); -} - -template -void Unserialize(Stream& is, CScript& v, int nType, int nVersion) -{ - Unserialize(is, (std::vector&)v, nType, nVersion); -} - - - -// -// pair -// -template -unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) -{ - return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); -} - -template -void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) -{ - Serialize(os, item.first, nType, nVersion); - Serialize(os, item.second, nType, nVersion); -} - -template -void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) -{ - Unserialize(is, item.first, nType, nVersion); - Unserialize(is, item.second, nType, nVersion); -} - - - -// -// 3 tuple -// -template -unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); -} - -template -void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); -} - - - -// -// 4 tuple -// -template -unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); - Serialize(os, boost::get<3>(item), nType, nVersion); -} - -template -void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); - Unserialize(is, boost::get<3>(item), nType, nVersion); -} - - - -// -// map -// -template -unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) -{ - unsigned int nSize = GetSizeOfCompactSize(m.size()); - for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) - nSize += GetSerializeSize((*mi), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const std::map& m, int nType, int nVersion) -{ - WriteCompactSize(os, m.size()); - for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) - Serialize(os, (*mi), nType, nVersion); -} - -template -void Unserialize(Stream& is, std::map& m, int nType, int nVersion) -{ - m.clear(); - unsigned int nSize = ReadCompactSize(is); - typename std::map::iterator mi = m.begin(); - for (unsigned int i = 0; i < nSize; i++) - { - std::pair item; - Unserialize(is, item, nType, nVersion); - mi = m.insert(mi, item); - } -} - - - -// -// set -// -template -unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) -{ - unsigned int nSize = GetSizeOfCompactSize(m.size()); - for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) - nSize += GetSerializeSize((*it), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const std::set& m, int nType, int nVersion) -{ - WriteCompactSize(os, m.size()); - for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) - Serialize(os, (*it), nType, nVersion); -} - -template -void Unserialize(Stream& is, std::set& m, int nType, int nVersion) -{ - m.clear(); - unsigned int nSize = ReadCompactSize(is); - typename std::set::iterator it = m.begin(); - for (unsigned int i = 0; i < nSize; i++) - { - K key; - Unserialize(is, key, nType, nVersion); - it = m.insert(it, key); - } -} - - - -// -// Support for IMPLEMENT_SERIALIZE and READWRITE macro -// -class CSerActionGetSerializeSize { }; -class CSerActionSerialize { }; -class CSerActionUnserialize { }; - -template -inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) -{ - return ::GetSerializeSize(obj, nType, nVersion); -} - -template -inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) -{ - ::Serialize(s, obj, nType, nVersion); - return 0; -} - -template -inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) -{ - ::Unserialize(s, obj, nType, nVersion); - return 0; -} - -struct ser_streamplaceholder -{ - int nType; - int nVersion; -}; - - - - - - - - - - - -typedef std::vector > CSerializeData; - -/** Double ended buffer combining vector and stream-like interfaces. - * - * >> and << read and write unformatted data using the above serialization templates. - * Fills with data in linear time; some stringstream implementations take N^2 time. - */ -class CDataStream -{ -protected: - typedef CSerializeData vector_type; - vector_type vch; - unsigned int nReadPos; - short state; - short exceptmask; -public: - int nType; - int nVersion; - - typedef vector_type::allocator_type allocator_type; - typedef vector_type::size_type size_type; - typedef vector_type::difference_type difference_type; - typedef vector_type::reference reference; - typedef vector_type::const_reference const_reference; - typedef vector_type::value_type value_type; - typedef vector_type::iterator iterator; - typedef vector_type::const_iterator const_iterator; - typedef vector_type::reverse_iterator reverse_iterator; - - explicit CDataStream(int nTypeIn, int nVersionIn) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) - { - Init(nTypeIn, nVersionIn); - } - -#if !defined(_MSC_VER) || _MSC_VER >= 1300 - CDataStream(const char* pbegin, const char* pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) - { - Init(nTypeIn, nVersionIn); - } -#endif - - CDataStream(const vector_type& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const std::vector& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const std::vector& vchIn, int nTypeIn, int nVersionIn) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) - { - Init(nTypeIn, nVersionIn); - } - - void Init(int nTypeIn, int nVersionIn) - { - nReadPos = 0; - nType = nTypeIn; - nVersion = nVersionIn; - state = 0; - exceptmask = std::ios::badbit | std::ios::failbit; - } - - CDataStream& operator+=(const CDataStream& b) - { - vch.insert(vch.end(), b.begin(), b.end()); - return *this; - } - - friend CDataStream operator+(const CDataStream& a, const CDataStream& b) - { - CDataStream ret = a; - ret += b; - return (ret); - } - - std::string str() const - { - return (std::string(begin(), end())); - } - - - // - // Vector subset - // - const_iterator begin() const { return vch.begin() + nReadPos; } - iterator begin() { return vch.begin() + nReadPos; } - const_iterator end() const { return vch.end(); } - iterator end() { return vch.end(); } - size_type size() const { return vch.size() - nReadPos; } - bool empty() const { return vch.size() == nReadPos; } - void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } - void reserve(size_type n) { vch.reserve(n + nReadPos); } - const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } - reference operator[](size_type pos) { return vch[pos + nReadPos]; } - void clear() { vch.clear(); nReadPos = 0; } - iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } - void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } - - void insert(iterator it, const_iterator first, const_iterator last) - { - assert(last - first >= 0); - if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - - void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) - { - assert(last - first >= 0); - if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - -#if !defined(_MSC_VER) || _MSC_VER >= 1300 - void insert(iterator it, const char* first, const char* last) - { - assert(last - first >= 0); - if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } -#endif - - iterator erase(iterator it) - { - if (it == vch.begin() + nReadPos) - { - // special case for erasing from the front - if (++nReadPos >= vch.size()) - { - // whenever we reach the end, we take the opportunity to clear the buffer - nReadPos = 0; - return vch.erase(vch.begin(), vch.end()); - } - return vch.begin() + nReadPos; - } - else - return vch.erase(it); - } - - iterator erase(iterator first, iterator last) - { - if (first == vch.begin() + nReadPos) - { - // special case for erasing from the front - if (last == vch.end()) - { - nReadPos = 0; - return vch.erase(vch.begin(), vch.end()); - } - else - { - nReadPos = (last - vch.begin()); - return last; - } - } - else - return vch.erase(first, last); - } - - inline void Compact() - { - vch.erase(vch.begin(), vch.begin() + nReadPos); - nReadPos = 0; - } - - bool Rewind(size_type n) - { - // Rewind by n characters if the buffer hasn't been compacted yet - if (n > nReadPos) - return false; - nReadPos -= n; - return true; - } - - - // - // Stream subset - // - void setstate(short bits, const char* psz) - { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - bool eof() const { return size() == 0; } - bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } - bool good() const { return !eof() && (state == 0); } - void clear(short n) { state = n; } // name conflict with vector clear() - short exceptions() { return exceptmask; } - short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } - CDataStream* rdbuf() { return this; } - int in_avail() { return size(); } - - void SetType(int n) { nType = n; } - int GetType() { return nType; } - void SetVersion(int n) { nVersion = n; } - int GetVersion() { return nVersion; } - void ReadVersion() { *this >> nVersion; } - void WriteVersion() { *this << nVersion; } - - CDataStream& read(char* pch, int nSize) - { - // Read from the beginning of the buffer - assert(nSize >= 0); - unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) - { - if (nReadPosNext > vch.size()) - { - setstate(std::ios::failbit, "CDataStream::read() : end of data"); - memset(pch, 0, nSize); - nSize = vch.size() - nReadPos; - } - memcpy(pch, &vch[nReadPos], nSize); - nReadPos = 0; - vch.clear(); - return (*this); - } - memcpy(pch, &vch[nReadPos], nSize); - nReadPos = nReadPosNext; - return (*this); - } - - CDataStream& ignore(int nSize) - { - // Ignore from the beginning of the buffer - assert(nSize >= 0); - unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) - { - if (nReadPosNext > vch.size()) - { - setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); - nSize = vch.size() - nReadPos; - } - nReadPos = 0; - vch.clear(); - return (*this); - } - nReadPos = nReadPosNext; - return (*this); - } - - CDataStream& write(const char* pch, int nSize) - { - // Write to the end of the buffer - assert(nSize >= 0); - vch.insert(vch.end(), pch, pch + nSize); - return (*this); - } - - template - void Serialize(Stream& s, int nType, int nVersion) const - { - // Special case: stream << stream concatenates like stream += stream - if (!vch.empty()) - s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); - } - - template - unsigned int GetSerializeSize(const T& obj) - { - // Tells the size of the object if serialized to this stream - return ::GetSerializeSize(obj, nType, nVersion); - } - - template - CDataStream& operator<<(const T& obj) - { - // Serialize to this stream - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } - - template - CDataStream& operator>>(T& obj) - { - // Unserialize from this stream - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } - - void GetAndClear(CSerializeData &data) { - vch.swap(data); - CSerializeData().swap(vch); - } -}; - - - - - - - - - - -/** RAII wrapper for FILE*. - * - * Will automatically close the file when it goes out of scope if not null. - * If you're returning the file pointer, return file.release(). - * If you need to close the file early, use file.fclose() instead of fclose(file). - */ -class CAutoFile -{ -protected: - FILE* file; - short state; - short exceptmask; -public: - int nType; - int nVersion; - - CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) - { - file = filenew; - nType = nTypeIn; - nVersion = nVersionIn; - state = 0; - exceptmask = std::ios::badbit | std::ios::failbit; - } - - ~CAutoFile() - { - fclose(); - } - - void fclose() - { - if (file != NULL && file != stdin && file != stdout && file != stderr) - ::fclose(file); - file = NULL; - } - - FILE* release() { FILE* ret = file; file = NULL; return ret; } - operator FILE*() { return file; } - FILE* operator->() { return file; } - FILE& operator*() { return *file; } - FILE** operator&() { return &file; } - FILE* operator=(FILE* pnew) { return file = pnew; } - bool operator!() { return (file == NULL); } - - - // - // Stream subset - // - void setstate(short bits, const char* psz) - { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } - bool good() const { return state == 0; } - void clear(short n = 0) { state = n; } - short exceptions() { return exceptmask; } - short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } - - void SetType(int n) { nType = n; } - int GetType() { return nType; } - void SetVersion(int n) { nVersion = n; } - int GetVersion() { return nVersion; } - void ReadVersion() { *this >> nVersion; } - void WriteVersion() { *this << nVersion; } - - CAutoFile& read(char* pch, size_t nSize) - { - if (!file) - throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); - if (fread(pch, 1, nSize, file) != nSize) - setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); - return (*this); - } - - CAutoFile& write(const char* pch, size_t nSize) - { - if (!file) - throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); - if (fwrite(pch, 1, nSize, file) != nSize) - setstate(std::ios::failbit, "CAutoFile::write : write failed"); - return (*this); - } - - template - unsigned int GetSerializeSize(const T& obj) - { - // Tells the size of the object if serialized to this stream - return ::GetSerializeSize(obj, nType, nVersion); - } - - template - CAutoFile& operator<<(const T& obj) - { - // Serialize to this stream - if (!file) - throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } - - template - CAutoFile& operator>>(T& obj) - { - // Unserialize from this stream - if (!file) - throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } -}; - -/** Wrapper around a FILE* that implements a ring buffer to - * deserialize from. It guarantees the ability to rewind - * a given number of bytes. */ -class CBufferedFile -{ -private: - FILE *src; // source file - uint64 nSrcPos; // how many bytes have been read from source - uint64 nReadPos; // how many bytes have been read from this - uint64 nReadLimit; // up to which position we're allowed to read - uint64 nRewind; // how many bytes we guarantee to rewind - std::vector vchBuf; // the buffer - - short state; - short exceptmask; - -protected: - void setstate(short bits, const char *psz) { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - // read data from the source to fill the buffer - bool Fill() { - unsigned int pos = nSrcPos % vchBuf.size(); - unsigned int readNow = vchBuf.size() - pos; - unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; - if (nAvail < readNow) - readNow = nAvail; - if (readNow == 0) - return false; - size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); - if (read == 0) { - setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed"); - return false; - } else { - nSrcPos += read; - return true; - } - } - -public: - int nType; - int nVersion; - - CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) : - src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), - state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { - } - - // check whether no error occurred - bool good() const { - return state == 0; - } - - // check whether we're at the end of the source file - bool eof() const { - return nReadPos == nSrcPos && feof(src); - } - - // read a number of bytes - CBufferedFile& read(char *pch, size_t nSize) { - if (nSize + nReadPos > nReadLimit) - throw std::ios_base::failure("Read attempted past buffer limit"); - if (nSize + nRewind > vchBuf.size()) - throw std::ios_base::failure("Read larger than buffer size"); - while (nSize > 0) { - if (nReadPos == nSrcPos) - Fill(); - unsigned int pos = nReadPos % vchBuf.size(); - size_t nNow = nSize; - if (nNow + pos > vchBuf.size()) - nNow = vchBuf.size() - pos; - if (nNow + nReadPos > nSrcPos) - nNow = nSrcPos - nReadPos; - memcpy(pch, &vchBuf[pos], nNow); - nReadPos += nNow; - pch += nNow; - nSize -= nNow; - } - return (*this); - } - - // return the current reading position - uint64 GetPos() { - return nReadPos; - } - - // rewind to a given reading position - bool SetPos(uint64 nPos) { - nReadPos = nPos; - if (nReadPos + nRewind < nSrcPos) { - nReadPos = nSrcPos - nRewind; - return false; - } else if (nReadPos > nSrcPos) { - nReadPos = nSrcPos; - return false; - } else { - return true; - } - } - - bool Seek(uint64 nPos) { - long nLongPos = nPos; - if (nPos != (uint64)nLongPos) - return false; - if (fseek(src, nLongPos, SEEK_SET)) - return false; - nLongPos = ftell(src); - nSrcPos = nLongPos; - nReadPos = nLongPos; - state = 0; - return true; - } - - // prevent reading beyond a certain position - // no argument removes the limit - bool SetLimit(uint64 nPos = (uint64)(-1)) { - if (nPos < nReadPos) - return false; - nReadLimit = nPos; - return true; - } - - template - CBufferedFile& operator>>(T& obj) { - // Unserialize from this stream - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } - - // search for a given byte in the stream, and remain positioned on it - void FindByte(char ch) { - while (true) { - if (nReadPos == nSrcPos) - Fill(); - if (vchBuf[nReadPos % vchBuf.size()] == ch) - break; - nReadPos++; - } - } -}; - -#endif diff --git a/src/sync.cpp b/src/sync.cpp deleted file mode 100644 index 1ac4403b..00000000 --- a/src/sync.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2011-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "sync.h" -#include "util.h" - -#include - -#ifdef DEBUG_LOCKCONTENTION -void PrintLockContention(const char* pszName, const char* pszFile, int nLine) -{ - printf("LOCKCONTENTION: %s\n", pszName); - printf("Locker: %s:%d\n", pszFile, nLine); -} -#endif /* DEBUG_LOCKCONTENTION */ - -#ifdef DEBUG_LOCKORDER -// -// Early deadlock detection. -// Problem being solved: -// Thread 1 locks A, then B, then C -// Thread 2 locks D, then C, then A -// --> may result in deadlock between the two threads, depending on when they run. -// Solution implemented here: -// Keep track of pairs of locks: (A before B), (A before C), etc. -// Complain if any thread tries to lock in a different order. -// - -struct CLockLocation -{ - CLockLocation(const char* pszName, const char* pszFile, int nLine) - { - mutexName = pszName; - sourceFile = pszFile; - sourceLine = nLine; - } - - std::string ToString() const - { - return mutexName+" "+sourceFile+":"+itostr(sourceLine); - } - -private: - std::string mutexName; - std::string sourceFile; - int sourceLine; -}; - -typedef std::vector< std::pair > LockStack; - -static boost::mutex dd_mutex; -static std::map, LockStack> lockorders; -static boost::thread_specific_ptr lockstack; - - -static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) -{ - printf("POTENTIAL DEADLOCK DETECTED\n"); - printf("Previous lock order was:\n"); - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) - { - if (i.first == mismatch.first) printf(" (1)"); - if (i.first == mismatch.second) printf(" (2)"); - printf(" %s\n", i.second.ToString().c_str()); - } - printf("Current lock order is:\n"); - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) - { - if (i.first == mismatch.first) printf(" (1)"); - if (i.first == mismatch.second) printf(" (2)"); - printf(" %s\n", i.second.ToString().c_str()); - } -} - -static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) -{ - if (lockstack.get() == NULL) - lockstack.reset(new LockStack); - - if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); - dd_mutex.lock(); - - (*lockstack).push_back(std::make_pair(c, locklocation)); - - if (!fTry) { - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) { - if (i.first == c) break; - - std::pair p1 = std::make_pair(i.first, c); - if (lockorders.count(p1)) - continue; - lockorders[p1] = (*lockstack); - - std::pair p2 = std::make_pair(c, i.first); - if (lockorders.count(p2)) - { - potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); - break; - } - } - } - dd_mutex.unlock(); -} - -static void pop_lock() -{ - if (fDebug) - { - const CLockLocation& locklocation = (*lockstack).rbegin()->second; - printf("Unlocked: %s\n", locklocation.ToString().c_str()); - } - dd_mutex.lock(); - (*lockstack).pop_back(); - dd_mutex.unlock(); -} - -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) -{ - push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); -} - -void LeaveCritical() -{ - pop_lock(); -} - -#endif /* DEBUG_LOCKORDER */ diff --git a/src/txdb.cpp b/src/txdb.cpp deleted file mode 100644 index 3b3b57e4..00000000 --- a/src/txdb.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2011-2013 PPCoin developers -// Copyright (c) 2013 The Primecoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "txdb.h" -#include "main.h" -#include "hash.h" - -using namespace std; - -void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { - if (coins.IsPruned()) - batch.Erase(make_pair('c', hash)); - else - batch.Write(make_pair('c', hash), coins); -} - -void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { - batch.Write('B', hash); -} - -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { -} - -bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { - return db.Read(make_pair('c', txid), coins); -} - -bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { - CLevelDBBatch batch; - BatchWriteCoins(batch, txid, coins); - return db.WriteBatch(batch); -} - -bool CCoinsViewDB::HaveCoins(const uint256 &txid) { - return db.Exists(make_pair('c', txid)); -} - -CBlockIndex *CCoinsViewDB::GetBestBlock() { - uint256 hashBestChain; - if (!db.Read('B', hashBestChain)) - return NULL; - std::map::iterator it = mapBlockIndex.find(hashBestChain); - if (it == mapBlockIndex.end()) - return NULL; - return it->second; -} - -bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { - CLevelDBBatch batch; - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); - return db.WriteBatch(batch); -} - -bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { - printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); - - CLevelDBBatch batch; - for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) - BatchWriteCoins(batch, it->first, it->second); - if (pindex) - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); - - return db.WriteBatch(batch); -} - -CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { -} - -bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) -{ - return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); -} - -bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) -{ - return Read('I', bnBestInvalidWork); -} - -bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) -{ - return Write('I', bnBestInvalidWork); -} - -bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) { - return Write(make_pair('f', nFile), info); -} - -bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { - return Read(make_pair('f', nFile), info); -} - -bool CBlockTreeDB::WriteLastBlockFile(int nFile) { - return Write('l', nFile); -} - -bool CBlockTreeDB::WriteReindexing(bool fReindexing) { - if (fReindexing) - return Write('R', '1'); - else - return Erase('R'); -} - -bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { - fReindexing = Exists('R'); - return true; -} - -bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { - return Read('l', nFile); -} - -bool CCoinsViewDB::GetStats(CCoinsStats &stats) { - leveldb::Iterator *pcursor = db.NewIterator(); - pcursor->SeekToFirst(); - - CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = GetBestBlock()->GetBlockHash(); - ss << stats.hashBlock; - int64 nTotalAmount = 0; - while (pcursor->Valid()) { - boost::this_thread::interruption_point(); - try { - leveldb::Slice slKey = pcursor->key(); - CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); - char chType; - ssKey >> chType; - if (chType == 'c') { - leveldb::Slice slValue = pcursor->value(); - CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); - CCoins coins; - ssValue >> coins; - uint256 txhash; - ssKey >> txhash; - ss << txhash; - ss << VARINT(coins.nVersion); - ss << (coins.fCoinBase ? 'c' : 'n'); - ss << VARINT(coins.nHeight); - stats.nTransactions++; - for (unsigned int i=0; iNext(); - } catch (std::exception &e) { - return error("%s() : deserialize error", __PRETTY_FUNCTION__); - } - } - delete pcursor; - stats.nHeight = GetBestBlock()->nHeight; - stats.hashSerialized = ss.GetHash(); - stats.nTotalAmount = nTotalAmount; - return true; -} - -bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { - return Read(make_pair('t', txid), pos); -} - -bool CBlockTreeDB::WriteTxIndex(const std::vector >&vect) { - CLevelDBBatch batch; - for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) - batch.Write(make_pair('t', it->first), it->second); - return WriteBatch(batch); -} - -bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { - return Write(std::make_pair('F', name), fValue ? '1' : '0'); -} - -bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { - char ch; - if (!Read(std::make_pair('F', name), ch)) - return false; - fValue = ch == '1'; - return true; -} - -bool CBlockTreeDB::LoadBlockIndexGuts() -{ - leveldb::Iterator *pcursor = NewIterator(); - - CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); - ssKeySet << make_pair('b', uint256(0)); - pcursor->Seek(ssKeySet.str()); - - // Load mapBlockIndex - while (pcursor->Valid()) { - boost::this_thread::interruption_point(); - try { - leveldb::Slice slKey = pcursor->key(); - CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); - char chType; - ssKey >> chType; - if (chType == 'b') { - leveldb::Slice slValue = pcursor->value(); - CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); - CDiskBlockIndex diskindex; - ssValue >> diskindex; - - // Construct block index object - CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); - pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); - pindexNew->nPrimeChainType = diskindex.nPrimeChainType; - pindexNew->nPrimeChainLength = diskindex.nPrimeChainLength; - pindexNew->nMoneySupply = diskindex.nMoneySupply; - pindexNew->nHeight = diskindex.nHeight; - pindexNew->nFile = diskindex.nFile; - pindexNew->nDataPos = diskindex.nDataPos; - pindexNew->nUndoPos = diskindex.nUndoPos; - pindexNew->nVersion = diskindex.nVersion; - pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; - pindexNew->nTime = diskindex.nTime; - pindexNew->nBits = diskindex.nBits; - pindexNew->nNonce = diskindex.nNonce; - pindexNew->nStatus = diskindex.nStatus; - pindexNew->nTx = diskindex.nTx; - - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) - pindexGenesisBlock = pindexNew; - - if (!pindexNew->CheckIndex()) - return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); - - pcursor->Next(); - } else { - break; // if shutdown requested or finished loading block index - } - } catch (std::exception &e) { - return error("%s() : deserialize error", __PRETTY_FUNCTION__); - } - } - delete pcursor; - - return true; -} - -bool CBlockTreeDB::ReadSyncCheckpoint(uint256& hashCheckpoint) -{ - return Read(string("hashSyncCheckpoint"), hashCheckpoint); -} - -bool CBlockTreeDB::WriteSyncCheckpoint(uint256 hashCheckpoint) -{ - return Write(string("hashSyncCheckpoint"), hashCheckpoint); -} - -bool CBlockTreeDB::ReadCheckpointPubKey(string& strPubKey) -{ - return Read(string("strCheckpointPubKey"), strPubKey); -} - -bool CBlockTreeDB::WriteCheckpointPubKey(const string& strPubKey) -{ - return Write(string("strCheckpointPubKey"), strPubKey); -} - diff --git a/src/txdb.h b/src/txdb.h deleted file mode 100644 index 5fb553f8..00000000 --- a/src/txdb.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2011-2013 PPCoin developers -// Copyright (c) 2013 Primecoin developers -// Distributed under conditional MIT/X11 software license, -// see the accompanying file COPYING. -#ifndef BITCOIN_TXDB_LEVELDB_H -#define BITCOIN_TXDB_LEVELDB_H - -#include "main.h" -#include "leveldb.h" - -/** CCoinsView backed by the LevelDB coin database (chainstate/) */ -class CCoinsViewDB : public CCoinsView -{ -protected: - CLevelDB db; -public: - CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); - bool GetStats(CCoinsStats &stats); -}; - -/** Access to the block database (blocks/index/) */ -class CBlockTreeDB : public CLevelDB -{ -public: - CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); -private: - CBlockTreeDB(const CBlockTreeDB&); - void operator=(const CBlockTreeDB&); -public: - bool WriteBlockIndex(const CDiskBlockIndex& blockindex); - bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); - bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork); - bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); - bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); - bool ReadLastBlockFile(int &nFile); - bool WriteLastBlockFile(int nFile); - bool WriteReindexing(bool fReindex); - bool ReadReindexing(bool &fReindex); - bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); - bool WriteTxIndex(const std::vector > &list); - bool WriteFlag(const std::string &name, bool fValue); - bool ReadFlag(const std::string &name, bool &fValue); - bool LoadBlockIndexGuts(); - - // ppcoin sync checkpoint related data - bool ReadSyncCheckpoint(uint256& hashCheckpoint); - bool WriteSyncCheckpoint(uint256 hashCheckpoint); - bool ReadCheckpointPubKey(std::string& strPubKey); - bool WriteCheckpointPubKey(const std::string& strPubKey); -}; - -#endif // BITCOIN_TXDB_LEVELDB_H diff --git a/src/ui_interface.h b/src/ui_interface.h deleted file mode 100644 index 5b0555c1..00000000 --- a/src/ui_interface.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_UI_INTERFACE_H -#define BITCOIN_UI_INTERFACE_H - -#include -#include "util.h" // for int64 -#include -#include - -class CBasicKeyStore; -class CWallet; -class uint256; - -/** General change type (added, updated, removed). */ -enum ChangeType -{ - CT_NEW, - CT_UPDATED, - CT_DELETED -}; - -/** Signals for UI communication. */ -class CClientUIInterface -{ -public: - /** Flags for CClientUIInterface::ThreadSafeMessageBox */ - enum MessageBoxFlags - { - ICON_INFORMATION = 0, - ICON_WARNING = (1U << 0), - ICON_ERROR = (1U << 1), - /** - * Mask of all available icons in CClientUIInterface::MessageBoxFlags - * This needs to be updated, when icons are changed there! - */ - ICON_MASK = (ICON_INFORMATION | ICON_WARNING | ICON_ERROR), - - /** These values are taken from qmessagebox.h "enum StandardButton" to be directly usable */ - BTN_OK = 0x00000400U, // QMessageBox::Ok - BTN_YES = 0x00004000U, // QMessageBox::Yes - BTN_NO = 0x00010000U, // QMessageBox::No - BTN_ABORT = 0x00040000U, // QMessageBox::Abort - BTN_RETRY = 0x00080000U, // QMessageBox::Retry - BTN_IGNORE = 0x00100000U, // QMessageBox::Ignore - BTN_CLOSE = 0x00200000U, // QMessageBox::Close - BTN_CANCEL = 0x00400000U, // QMessageBox::Cancel - BTN_DISCARD = 0x00800000U, // QMessageBox::Discard - BTN_HELP = 0x01000000U, // QMessageBox::Help - BTN_APPLY = 0x02000000U, // QMessageBox::Apply - BTN_RESET = 0x04000000U, // QMessageBox::Reset - /** - * Mask of all available buttons in CClientUIInterface::MessageBoxFlags - * This needs to be updated, when buttons are changed there! - */ - BTN_MASK = (BTN_OK | BTN_YES | BTN_NO | BTN_ABORT | BTN_RETRY | BTN_IGNORE | - BTN_CLOSE | BTN_CANCEL | BTN_DISCARD | BTN_HELP | BTN_APPLY | BTN_RESET), - - /** Force blocking, modal message box dialog (not just OS notification) */ - MODAL = 0x10000000U, - - /** Predefined combinations for certain default usage cases */ - MSG_INFORMATION = ICON_INFORMATION, - MSG_WARNING = (ICON_WARNING | BTN_OK | MODAL), - MSG_ERROR = (ICON_ERROR | BTN_OK | MODAL) - }; - - /** Show message box. */ - boost::signals2::signal > ThreadSafeMessageBox; - - /** Ask the user whether they want to pay a fee or not. */ - boost::signals2::signal > ThreadSafeAskFee; - - /** Handle a URL passed at the command line. */ - boost::signals2::signal ThreadSafeHandleURI; - - /** Progress message during initialization. */ - boost::signals2::signal InitMessage; - - /** Translate a message to the native language of the user. */ - boost::signals2::signal Translate; - - /** Block chain changed. */ - boost::signals2::signal NotifyBlocksChanged; - - /** Number of network connections changed. */ - boost::signals2::signal NotifyNumConnectionsChanged; - - /** - * New, updated or cancelled alert. - * @note called with lock cs_mapAlerts held. - */ - boost::signals2::signal NotifyAlertChanged; -}; - -extern CClientUIInterface uiInterface; - -/** - * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. - * If no translation slot is registered, nothing is returned, and simply return the input. - */ -inline std::string _(const char* psz) -{ - boost::optional rv = uiInterface.Translate(psz); - return rv ? (*rv) : psz; -} - -#endif diff --git a/src/util.cpp b/src/util.cpp index d26759a7..5bb06819 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -15,9 +15,8 @@ #endif #include "util.h" -#include "sync.h" #include "version.h" -#include "ui_interface.h" +#include "allocators.h" #include #include // for to_lower() #include // for startswith() and endswith() @@ -68,6 +67,7 @@ namespace boost { #endif using namespace std; +using namespace boost; map mapArgs; map > mapMultiArgs; @@ -1302,59 +1302,6 @@ int64 GetAdjustedTime() return GetTime() + GetTimeOffset(); } -void AddTimeData(const CNetAddr& ip, int64 nTime) -{ - int64 nOffsetSample = nTime - GetTime(); - - // Ignore duplicates - static set setKnown; - if (!setKnown.insert(ip).second) - return; - - // Add data - vTimeOffsets.input(nOffsetSample); - printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); - if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) - { - int64 nMedian = vTimeOffsets.median(); - std::vector vSorted = vTimeOffsets.sorted(); - // Only let other nodes change our time by so much - if (abs64(nMedian) < 70 * 60) - { - nTimeOffset = nMedian; - } - else - { - nTimeOffset = 0; - - static bool fDone; - if (!fDone) - { - // If nobody has a time different than ours but within 5 minutes of ours, give a warning - bool fMatch = false; - BOOST_FOREACH(int64 nOffset, vSorted) - if (nOffset != 0 && abs64(nOffset) < 5 * 60) - fMatch = true; - - if (!fMatch) - { - fDone = true; - string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Primecoin will not work properly."); - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); - } - } - } - if (fDebug) { - BOOST_FOREACH(int64 n, vSorted) - printf("%+"PRI64d" ", n); - printf("| "); - } - printf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); - } -} - uint32_t insecure_rand_Rz = 11; uint32_t insecure_rand_Rw = 11; void seed_insecure_rand(bool fDeterministic) diff --git a/src/util.h b/src/util.h index 855403b8..45ef0517 100644 --- a/src/util.h +++ b/src/util.h @@ -7,6 +7,7 @@ #define BITCOIN_UTIL_H #include "uint256.h" +#include "sync.h" #include diff --git a/src/wallet.cpp b/src/wallet.cpp deleted file mode 100644 index 872afaee..00000000 --- a/src/wallet.cpp +++ /dev/null @@ -1,1886 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "wallet.h" -#include "walletdb.h" -#include "crypter.h" -#include "ui_interface.h" -#include "base58.h" -#include - -using namespace std; - - -////////////////////////////////////////////////////////////////////////////// -// -// mapWallet -// - -struct CompareValueOnly -{ - bool operator()(const pair >& t1, - const pair >& t2) const - { - return t1.first < t2.first; - } -}; - -CPubKey CWallet::GenerateNewKey() -{ - bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets - - RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(fCompressed); - - // Compressed public keys were introduced in version 0.6.0 - if (fCompressed) - SetMinVersion(FEATURE_COMPRPUBKEY); - - if (!AddKey(key)) - throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); -} - -bool CWallet::AddKey(const CKey& key) -{ - if (!CCryptoKeyStore::AddKey(key)) - return false; - if (!fFileBacked) - return true; - if (!IsCrypted()) - return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); - return true; -} - -bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) -{ - if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) - return false; - if (!fFileBacked) - return true; - { - LOCK(cs_wallet); - if (pwalletdbEncryption) - return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); - else - return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); - } - return false; -} - -bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) -{ - return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); -} - -bool CWallet::AddCScript(const CScript& redeemScript) -{ - if (!CCryptoKeyStore::AddCScript(redeemScript)) - return false; - if (!fFileBacked) - return true; - return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); -} - -bool CWallet::Unlock(const SecureString& strWalletPassphrase) -{ - if (!IsLocked()) - return false; - - CCrypter crypter; - CKeyingMaterial vMasterKey; - - { - LOCK(cs_wallet); - BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) - { - if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) - return false; - if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) - return false; - if (CCryptoKeyStore::Unlock(vMasterKey)) - return true; - } - } - return false; -} - -bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) -{ - bool fWasLocked = IsLocked(); - - { - LOCK(cs_wallet); - Lock(); - - CCrypter crypter; - CKeyingMaterial vMasterKey; - BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) - { - if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) - return false; - if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) - return false; - if (CCryptoKeyStore::Unlock(vMasterKey)) - { - int64 nStartTime = GetTimeMillis(); - crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); - - nStartTime = GetTimeMillis(); - crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; - - if (pMasterKey.second.nDeriveIterations < 25000) - pMasterKey.second.nDeriveIterations = 25000; - - printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); - - if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) - return false; - if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) - return false; - CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); - if (fWasLocked) - Lock(); - return true; - } - } - } - - return false; -} - -void CWallet::SetBestChain(const CBlockLocator& loc) -{ - CWalletDB walletdb(strWalletFile); - walletdb.WriteBestBlock(loc); -} - -// This class implements an addrIncoming entry that causes pre-0.4 -// clients to crash on startup if reading a private-key-encrypted wallet. -class CCorruptAddress -{ -public: - IMPLEMENT_SERIALIZE - ( - if (nType & SER_DISK) - READWRITE(nVersion); - ) -}; - -bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) -{ - if (nWalletVersion >= nVersion) - return true; - - // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way - if (fExplicit && nVersion > nWalletMaxVersion) - nVersion = FEATURE_LATEST; - - nWalletVersion = nVersion; - - if (nVersion > nWalletMaxVersion) - nWalletMaxVersion = nVersion; - - if (fFileBacked) - { - CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); - if (nWalletVersion >= 40000) - { - // Versions prior to 0.4.0 did not support the "minversion" record. - // Use a CCorruptAddress to make them crash instead. - CCorruptAddress corruptAddress; - pwalletdb->WriteSetting("addrIncoming", corruptAddress); - } - if (nWalletVersion > 40000) - pwalletdb->WriteMinVersion(nWalletVersion); - if (!pwalletdbIn) - delete pwalletdb; - } - - return true; -} - -bool CWallet::SetMaxVersion(int nVersion) -{ - // cannot downgrade below current version - if (nWalletVersion > nVersion) - return false; - - nWalletMaxVersion = nVersion; - - return true; -} - -bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) -{ - if (IsCrypted()) - return false; - - CKeyingMaterial vMasterKey; - RandAddSeedPerfmon(); - - vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); - RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); - - CMasterKey kMasterKey; - - RandAddSeedPerfmon(); - kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); - RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); - - CCrypter crypter; - int64 nStartTime = GetTimeMillis(); - crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); - - nStartTime = GetTimeMillis(); - crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; - - if (kMasterKey.nDeriveIterations < 25000) - kMasterKey.nDeriveIterations = 25000; - - printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); - - if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) - return false; - if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) - return false; - - { - LOCK(cs_wallet); - mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; - if (fFileBacked) - { - pwalletdbEncryption = new CWalletDB(strWalletFile); - if (!pwalletdbEncryption->TxnBegin()) - return false; - pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); - } - - if (!EncryptKeys(vMasterKey)) - { - if (fFileBacked) - pwalletdbEncryption->TxnAbort(); - exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet. - } - - // Encryption was introduced in version 0.4.0 - SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); - - if (fFileBacked) - { - if (!pwalletdbEncryption->TxnCommit()) - exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. - - delete pwalletdbEncryption; - pwalletdbEncryption = NULL; - } - - Lock(); - Unlock(strWalletPassphrase); - NewKeyPool(); - Lock(); - - // Need to completely rewrite the wallet file; if we don't, bdb might keep - // bits of the unencrypted private key in slack space in the database file. - CDB::Rewrite(strWalletFile); - - } - NotifyStatusChanged(this); - - return true; -} - -int64 CWallet::IncOrderPosNext(CWalletDB *pwalletdb) -{ - int64 nRet = nOrderPosNext++; - if (pwalletdb) { - pwalletdb->WriteOrderPosNext(nOrderPosNext); - } else { - CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); - } - return nRet; -} - -CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, std::string strAccount) -{ - CWalletDB walletdb(strWalletFile); - - // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. - TxItems txOrdered; - - // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry - // would make this much faster for applications that do this a lot. - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); - } - acentries.clear(); - walletdb.ListAccountCreditDebit(strAccount, acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); - } - - return txOrdered; -} - -void CWallet::WalletUpdateSpent(const CTransaction &tx) -{ - // Anytime a signature is successfully verified, it's proof the outpoint is spent. - // Update the wallet spent flag if it doesn't know due to wallet.dat being - // restored from backup or the user making copies of wallet.dat. - { - LOCK(cs_wallet); - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - map::iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (txin.prevout.n >= wtx.vout.size()) - printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); - else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) - { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkSpent(txin.prevout.n); - wtx.WriteToDisk(); - NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); - } - } - } - } -} - -void CWallet::MarkDirty() -{ - { - LOCK(cs_wallet); - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - item.second.MarkDirty(); - } -} - -bool CWallet::AddToWallet(const CWalletTx& wtxIn) -{ - uint256 hash = wtxIn.GetHash(); - { - LOCK(cs_wallet); - // Inserts only if not already there, returns tx inserted or tx found - pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); - CWalletTx& wtx = (*ret.first).second; - wtx.BindWallet(this); - bool fInsertedNew = ret.second; - if (fInsertedNew) - { - wtx.nTimeReceived = GetAdjustedTime(); - wtx.nOrderPos = IncOrderPosNext(); - - wtx.nTimeSmart = wtx.nTimeReceived; - if (wtxIn.hashBlock != 0) - { - if (mapBlockIndex.count(wtxIn.hashBlock)) - { - unsigned int latestNow = wtx.nTimeReceived; - unsigned int latestEntry = 0; - { - // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future - int64 latestTolerated = latestNow + 300; - std::list acentries; - TxItems txOrdered = OrderedTxItems(acentries); - for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx == &wtx) - continue; - CAccountingEntry *const pacentry = (*it).second.second; - int64 nSmartTime; - if (pwtx) - { - nSmartTime = pwtx->nTimeSmart; - if (!nSmartTime) - nSmartTime = pwtx->nTimeReceived; - } - else - nSmartTime = pacentry->nTime; - if (nSmartTime <= latestTolerated) - { - latestEntry = nSmartTime; - if (nSmartTime > latestNow) - latestNow = nSmartTime; - break; - } - } - } - - unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime; - wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); - } - else - printf("AddToWallet() : found %s in block %s not in index\n", - wtxIn.GetHash().ToString().c_str(), - wtxIn.hashBlock.ToString().c_str()); - } - } - - bool fUpdated = false; - if (!fInsertedNew) - { - // Merge - if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) - { - wtx.hashBlock = wtxIn.hashBlock; - fUpdated = true; - } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) - { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; - wtx.nIndex = wtxIn.nIndex; - fUpdated = true; - } - if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) - { - wtx.fFromMe = wtxIn.fFromMe; - fUpdated = true; - } - fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); - } - - //// debug print - printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); - - // Write to disk - if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk()) - return false; -#ifndef QT_GUI - // If default receiving address gets used, replace it with a new one - if (vchDefaultKey.IsValid()) { - CScript scriptDefaultKey; - scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) - { - CPubKey newDefaultKey; - if (GetKeyFromPool(newDefaultKey, false)) - { - SetDefaultKey(newDefaultKey); - SetAddressBookName(vchDefaultKey.GetID(), ""); - } - } - } - } -#endif - // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins - WalletUpdateSpent(wtx); - - // Notify UI of new or updated transaction - NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); - - // notify an external script when a wallet transaction comes in or is updated - std::string strCmd = GetArg("-walletnotify", ""); - - if ( !strCmd.empty()) - { - boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free - } - - } - return true; -} - -// Add a transaction to the wallet, or update it. -// pblock is optional, but should be provided if the transaction is known to be in a block. -// If fUpdate is true, existing transactions will be updated. -bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) -{ - { - LOCK(cs_wallet); - bool fExisted = mapWallet.count(hash); - if (fExisted && !fUpdate) return false; - if (fExisted || IsMine(tx) || IsFromMe(tx)) - { - CWalletTx wtx(this,tx); - // Get merkle branch if transaction was found in a block - if (pblock) - wtx.SetMerkleBranch(pblock); - return AddToWallet(wtx); - } - else - WalletUpdateSpent(tx); - } - return false; -} - -bool CWallet::EraseFromWallet(uint256 hash) -{ - if (!fFileBacked) - return false; - { - LOCK(cs_wallet); - if (mapWallet.erase(hash)) - CWalletDB(strWalletFile).EraseTx(hash); - } - return true; -} - - -bool CWallet::IsMine(const CTxIn &txin) const -{ - { - LOCK(cs_wallet); - map::const_iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.vout.size()) - if (IsMine(prev.vout[txin.prevout.n])) - return true; - } - } - return false; -} - -int64 CWallet::GetDebit(const CTxIn &txin) const -{ - { - LOCK(cs_wallet); - map::const_iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.vout.size()) - if (IsMine(prev.vout[txin.prevout.n])) - return prev.vout[txin.prevout.n].nValue; - } - } - return 0; -} - -bool CWallet::IsChange(const CTxOut& txout) const -{ - CTxDestination address; - - // TODO: fix handling of 'change' outputs. The assumption is that any - // payment to a TX_PUBKEYHASH that is mine but isn't in the address book - // is change. That assumption is likely to break when we implement multisignature - // wallets that return change back into a multi-signature-protected address; - // a better way of identifying which outputs are 'the send' and which are - // 'the change' will need to be implemented (maybe extend CWalletTx to remember - // which output, if any, was change). - if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address)) - { - LOCK(cs_wallet); - if (!mapAddressBook.count(address)) - return true; - } - return false; -} - -int64 CWalletTx::GetTxTime() const -{ - int64 n = nTimeSmart; - return n ? n : nTimeReceived; -} - -int CWalletTx::GetRequestCount() const -{ - // Returns -1 if it wasn't being tracked - int nRequests = -1; - { - LOCK(pwallet->cs_wallet); - if (IsCoinBase()) - { - // Generated block - if (hashBlock != 0) - { - map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; - } - } - else - { - // Did anyone request this transaction? - map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); - if (mi != pwallet->mapRequestCount.end()) - { - nRequests = (*mi).second; - - // How about the block it's in? - if (nRequests == 0 && hashBlock != 0) - { - map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; - else - nRequests = 1; // If it's in someone else's block it must have got out - } - } - } - } - return nRequests; -} - -void CWalletTx::GetAmounts(list >& listReceived, - list >& listSent, int64& nFee, string& strSentAccount) const -{ - nFee = 0; - listReceived.clear(); - listSent.clear(); - strSentAccount = strFromAccount; - - // Compute fee: - int64 nDebit = GetDebit(); - if (nDebit > 0) // debit>0 means we signed/sent this transaction - { - int64 nValueOut = GetValueOut(); - nFee = nDebit - nValueOut; - } - - // Sent/received. - BOOST_FOREACH(const CTxOut& txout, vout) - { - CTxDestination address; - vector vchPubKey; - if (!ExtractDestination(txout.scriptPubKey, address)) - { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); - } - - // Don't report 'change' txouts - if (nDebit > 0 && pwallet->IsChange(txout)) - continue; - - if (nDebit > 0) - listSent.push_back(make_pair(address, txout.nValue)); - - if (pwallet->IsMine(txout)) - listReceived.push_back(make_pair(address, txout.nValue)); - } - -} - -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived, - int64& nSent, int64& nFee) const -{ - nReceived = nSent = nFee = 0; - - int64 allFee; - string strSentAccount; - list > listReceived; - list > listSent; - GetAmounts(listReceived, listSent, allFee, strSentAccount); - - if (strAccount == strSentAccount) - { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent) - nSent += s.second; - nFee = allFee; - } - { - LOCK(pwallet->cs_wallet); - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) - { - if (pwallet->mapAddressBook.count(r.first)) - { - map::const_iterator mi = pwallet->mapAddressBook.find(r.first); - if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) - nReceived += r.second; - } - else if (strAccount.empty()) - { - nReceived += r.second; - } - } - } -} - -void CWalletTx::AddSupportingTransactions() -{ - vtxPrev.clear(); - - const int COPY_DEPTH = 3; - if (SetMerkleBranch() < COPY_DEPTH) - { - vector vWorkQueue; - BOOST_FOREACH(const CTxIn& txin, vin) - vWorkQueue.push_back(txin.prevout.hash); - - { - LOCK(pwallet->cs_wallet); - map mapWalletPrev; - set setAlreadyDone; - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hash = vWorkQueue[i]; - if (setAlreadyDone.count(hash)) - continue; - setAlreadyDone.insert(hash); - - CMerkleTx tx; - map::const_iterator mi = pwallet->mapWallet.find(hash); - if (mi != pwallet->mapWallet.end()) - { - tx = (*mi).second; - BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) - mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; - } - else if (mapWalletPrev.count(hash)) - { - tx = *mapWalletPrev[hash]; - } - - int nDepth = tx.SetMerkleBranch(); - vtxPrev.push_back(tx); - - if (nDepth < COPY_DEPTH) - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - vWorkQueue.push_back(txin.prevout.hash); - } - } - } - } - - reverse(vtxPrev.begin(), vtxPrev.end()); -} - -bool CWalletTx::WriteToDisk() -{ - return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); -} - -// Scan the block chain (starting in pindexStart) for transactions -// from or to us. If fUpdate is true, found transactions that already -// exist in the wallet will be updated. -int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) -{ - int ret = 0; - - CBlockIndex* pindex = pindexStart; - { - LOCK(cs_wallet); - while (pindex) - { - CBlock block; - block.ReadFromDisk(pindex); - BOOST_FOREACH(CTransaction& tx, block.vtx) - { - if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) - ret++; - } - pindex = pindex->pnext; - } - } - return ret; -} - -void CWallet::ReacceptWalletTransactions() -{ - bool fRepeat = true; - while (fRepeat) - { - LOCK(cs_wallet); - fRepeat = false; - bool fMissing = false; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - if (wtx.IsCoinBase() && wtx.IsSpent(0)) - continue; - - CCoins coins; - bool fUpdated = false; - bool fFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); - if (fFound || wtx.GetDepthInMainChain() > 0) - { - // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat - for (unsigned int i = 0; i < wtx.vout.size(); i++) - { - if (wtx.IsSpent(i)) - continue; - if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i])) - { - wtx.MarkSpent(i); - fUpdated = true; - fMissing = true; - } - } - if (fUpdated) - { - printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkDirty(); - wtx.WriteToDisk(); - } - } - else - { - // Re-accept any txes of ours that aren't already in a block - if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(false); - } - } - if (fMissing) - { - // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) - fRepeat = true; // Found missing transactions: re-do re-accept. - } - } -} - -void CWalletTx::RelayWalletTransaction() -{ - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - if (tx.GetDepthInMainChain() == 0) - RelayTransaction((CTransaction)tx, tx.GetHash()); - } - if (!IsCoinBase()) - { - if (GetDepthInMainChain() == 0) { - uint256 hash = GetHash(); - printf("Relaying wtx %s\n", hash.ToString().c_str()); - RelayTransaction((CTransaction)*this, hash); - } - } -} - -void CWallet::ResendWalletTransactions() -{ - // Do this infrequently and randomly to avoid giving away - // that these are our transactions. - static int64 nNextTime; - if (GetTime() < nNextTime) - return; - bool fFirst = (nNextTime == 0); - nNextTime = GetTime() + GetRand(30 * 60); - if (fFirst) - return; - - // Only do it if there's been a new block since last time - static int64 nLastTime; - if (nTimeBestReceived < nLastTime) - return; - nLastTime = GetTime(); - - // Rebroadcast any of our txes that aren't in a block yet - printf("ResendWalletTransactions()\n"); - { - LOCK(cs_wallet); - // Sort them in chronological order - multimap mapSorted; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - // Don't rebroadcast until it's had plenty of time that - // it should have gotten in already by now. - if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) - mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); - } - BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) - { - CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(); - } - } -} - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Actions -// - - -int64 CWallet::GetBalance() const -{ - int64 nTotal = 0; - { - LOCK(cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsConfirmed()) - nTotal += pcoin->GetAvailableCredit(); - } - } - - return nTotal; -} - -int64 CWallet::GetUnconfirmedBalance() const -{ - int64 nTotal = 0; - { - LOCK(cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - nTotal += pcoin->GetAvailableCredit(); - } - } - return nTotal; -} - -int64 CWallet::GetImmatureBalance() const -{ - int64 nTotal = 0; - { - LOCK(cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - nTotal += pcoin->GetImmatureCredit(); - } - } - return nTotal; -} - -// populate vCoins with vector of spendable COutputs -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const -{ - vCoins.clear(); - - { - LOCK(cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - - if (!pcoin->IsFinal()) - continue; - - if (fOnlyConfirmed && !pcoin->IsConfirmed()) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && - !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0) - vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); - } - } - } -} - -static void ApproximateBestSubset(vector > >vValue, int64 nTotalLower, int64 nTargetValue, - vector& vfBest, int64& nBest, int iterations = 1000) -{ - vector vfIncluded; - - vfBest.assign(vValue.size(), true); - nBest = nTotalLower; - - seed_insecure_rand(); - - for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) - { - vfIncluded.assign(vValue.size(), false); - int64 nTotal = 0; - bool fReachedTarget = false; - for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) - { - for (unsigned int i = 0; i < vValue.size(); i++) - { - //The solver here uses a randomized algorithm, - //the randomness serves no real security purpose but is just - //needed to prevent degenerate behavior and it is important - //that the rng fast. We do not use a constant random sequence, - //because there may be some privacy improvement by making - //the selection random. - if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) - { - nTotal += vValue[i].first; - vfIncluded[i] = true; - if (nTotal >= nTargetValue) - { - fReachedTarget = true; - if (nTotal < nBest) - { - nBest = nTotal; - vfBest = vfIncluded; - } - nTotal -= vValue[i].first; - vfIncluded[i] = false; - } - } - } - } - } -} - -bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector vCoins, - set >& setCoinsRet, int64& nValueRet) const -{ - setCoinsRet.clear(); - nValueRet = 0; - - // List of values less than target - pair > coinLowestLarger; - coinLowestLarger.first = std::numeric_limits::max(); - coinLowestLarger.second.first = NULL; - vector > > vValue; - int64 nTotalLower = 0; - - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - - BOOST_FOREACH(COutput output, vCoins) - { - const CWalletTx *pcoin = output.tx; - - if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) - continue; - - int i = output.i; - int64 n = pcoin->vout[i].nValue; - - pair > coin = make_pair(n,make_pair(pcoin, i)); - - if (n == nTargetValue) - { - setCoinsRet.insert(coin.second); - nValueRet += coin.first; - return true; - } - else if (n < nTargetValue + CENT) - { - vValue.push_back(coin); - nTotalLower += n; - } - else if (n < coinLowestLarger.first) - { - coinLowestLarger = coin; - } - } - - if (nTotalLower == nTargetValue) - { - for (unsigned int i = 0; i < vValue.size(); ++i) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - return true; - } - - if (nTotalLower < nTargetValue) - { - if (coinLowestLarger.second.first == NULL) - return false; - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; - return true; - } - - // Solve subset sum by stochastic approximation - sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); - vector vfBest; - int64 nBest; - - ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); - if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) - ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); - - // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, - // or the next bigger coin is closer), return the bigger coin - if (coinLowestLarger.second.first && - ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest)) - { - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; - } - else { - for (unsigned int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - - //// debug print - printf("SelectCoins() best subset: "); - for (unsigned int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - printf("%s ", FormatMoney(vValue[i].first).c_str()); - printf("total %s\n", FormatMoney(nBest).c_str()); - } - - return true; -} - -bool CWallet::SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) const -{ - vector vCoins; - AvailableCoins(vCoins); - - return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)); -} - - - - -bool CWallet::CreateTransaction(const vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason) -{ - int64 nValue = 0; - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - { - if (nValue < 0) - { - strFailReason = _("Transaction amounts must be positive"); - return false; - } - nValue += s.second; - } - if (vecSend.empty() || nValue < 0) - { - strFailReason = _("Transaction amounts must be positive"); - return false; - } - - wtxNew.BindWallet(this); - - { - LOCK2(cs_main, cs_wallet); - { - nFeeRet = nTransactionFee; - loop - { - wtxNew.vin.clear(); - wtxNew.vout.clear(); - wtxNew.fFromMe = true; - - int64 nTotalValue = nValue + nFeeRet; - double dPriority = 0; - // vouts to the payees - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - { - CTxOut txout(s.second, s.first); - if (txout.IsDust()) - { - strFailReason = _("Transaction amount too small"); - return false; - } - wtxNew.vout.push_back(txout); - } - - // Choose coins to use - set > setCoins; - int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn)) - { - strFailReason = _("Insufficient funds"); - return false; - } - BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) - { - int64 nCredit = pcoin.first->vout[pcoin.second].nValue; - //The priority after the next block (depth+1) is used instead of the current, - //reflecting an assumption the user would accept a bit more delay for - //a chance at a free transaction. - dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); - } - - int64 nChange = nValueIn - nValue - nFeeRet; - // if sub-cent change is required, the fee must be raised to at least nMinTxFee - // or until nChange becomes zero - // NOTE: this depends on the exact behaviour of GetMinFee - if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) - { - int64 nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); - nChange -= nMoveToFee; - nFeeRet += nMoveToFee; - } - - // ppcoin: sub-cent change is moved to fee - if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT) - { - nFeeRet += nChange; - nChange = 0; - } - - if (nChange > 0) - { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - CPubKey vchPubKey; - assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked - - // Fill a vout to ourself - // TODO: pass in scriptChange instead of reservekey so - // change transaction isn't always pay-to-bitcoin-address - CScript scriptChange; - scriptChange.SetDestination(vchPubKey.GetID()); - - CTxOut newTxOut(nChange, scriptChange); - - // Never create dust outputs; if we would, just - // add the dust to the fee. - if (newTxOut.IsDust()) - { - nFeeRet += nChange; - reservekey.ReturnKey(); - } - else - { - // Insert change txn at random position: - vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); - wtxNew.vout.insert(position, newTxOut); - } - } - else - reservekey.ReturnKey(); - - // Fill vin - BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); - - // Sign - int nIn = 0; - BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) - { - strFailReason = _("Signing transaction failed"); - return false; - } - - // Limit size - unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); - if (nBytes >= MAX_STANDARD_TX_SIZE) - { - strFailReason = _("Transaction too large"); - return false; - } - dPriority /= nBytes; - - // Check that enough fee is included - int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND); - if (nFeeRet < max(nPayFee, nMinFee)) - { - nFeeRet = max(nPayFee, nMinFee); - continue; - } - - // Fill vtxPrev by copying from previous transactions vtxPrev - wtxNew.AddSupportingTransactions(); - wtxNew.fTimeReceivedIsTxTime = true; - - break; - } - } - } - return true; -} - -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason) -{ - vector< pair > vecSend; - vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason); -} - -// Call after CreateTransaction unless you want to abort -bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -{ - { - LOCK2(cs_main, cs_wallet); - printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); - { - // This is only to keep the database open to defeat the auto-flush for the - // duration of this scope. This is the only place where this optimization - // maybe makes sense; please don't do it anywhere else. - CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL; - - // Take key pair from key pool so it won't be used again - reservekey.KeepKey(); - - // Add tx to wallet, because if it has change it's also ours, - // otherwise just for transaction history. - AddToWallet(wtxNew); - - // Mark old coins as spent - set setCoins; - BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) - { - CWalletTx &coin = mapWallet[txin.prevout.hash]; - coin.BindWallet(this); - coin.MarkSpent(txin.prevout.n); - coin.WriteToDisk(); - NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); - } - - if (fFileBacked) - delete pwalletdb; - } - - // Track how many getdata requests our transaction gets - mapRequestCount[wtxNew.GetHash()] = 0; - - // Broadcast - if (!wtxNew.AcceptToMemoryPool(true, false)) - { - // This must not fail. The transaction has already been signed and recorded. - printf("CommitTransaction() : Error: Transaction not valid"); - return false; - } - wtxNew.RelayWalletTransaction(); - } - return true; -} - - - - -string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - CReserveKey reservekey(this); - int64 nFeeRequired; - - if (IsLocked()) - { - string strError = _("Error: Wallet locked, unable to create transaction!"); - printf("SendMoney() : %s", strError.c_str()); - return strError; - } - string strError; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError)) - { - if (nValue + nFeeRequired > GetBalance()) - strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); - printf("SendMoney() : %s\n", strError.c_str()); - return strError; - } - - if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)) - return "ABORTED"; - - if (!CommitTransaction(wtxNew, reservekey)) - return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); - - return ""; -} - - - -string CWallet::SendMoneyToDestination(const CTxDestination& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - // Check amount - if (nValue <= 0) - return _("Invalid amount"); - if (nValue + nTransactionFee > GetBalance()) - return _("Insufficient funds"); - - // Parse Bitcoin address - CScript scriptPubKey; - scriptPubKey.SetDestination(address); - - return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); -} - - - - -DBErrors CWallet::LoadWallet(bool& fFirstRunRet) -{ - if (!fFileBacked) - return DB_LOAD_OK; - fFirstRunRet = false; - DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); - if (nLoadWalletRet == DB_NEED_REWRITE) - { - if (CDB::Rewrite(strWalletFile, "\x04pool")) - { - setKeyPool.clear(); - // Note: can't top-up keypool here, because wallet is locked. - // User will be prompted to unlock wallet the next operation - // the requires a new key. - } - } - - if (nLoadWalletRet != DB_LOAD_OK) - return nLoadWalletRet; - fFirstRunRet = !vchDefaultKey.IsValid(); - - return DB_LOAD_OK; -} - - -bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) -{ - std::map::iterator mi = mapAddressBook.find(address); - mapAddressBook[address] = strName; - NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED); - if (!fFileBacked) - return false; - return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); -} - -bool CWallet::DelAddressBookName(const CTxDestination& address) -{ - mapAddressBook.erase(address); - NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), CT_DELETED); - if (!fFileBacked) - return false; - return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); -} - - -void CWallet::PrintWallet(const CBlock& block) -{ - { - LOCK(cs_wallet); - if (mapWallet.count(block.vtx[0].GetHash())) - { - CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); - } - } - printf("\n"); -} - -bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) -{ - { - LOCK(cs_wallet); - map::iterator mi = mapWallet.find(hashTx); - if (mi != mapWallet.end()) - { - wtx = (*mi).second; - return true; - } - } - return false; -} - -bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) -{ - if (fFileBacked) - { - if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) - return false; - } - vchDefaultKey = vchPubKey; - return true; -} - -bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) -{ - if (!pwallet->fFileBacked) - return false; - strWalletFileOut = pwallet->strWalletFile; - return true; -} - -// -// Mark old keypool keys as used, -// and generate all new keys -// -bool CWallet::NewKeyPool() -{ - { - LOCK(cs_wallet); - CWalletDB walletdb(strWalletFile); - BOOST_FOREACH(int64 nIndex, setKeyPool) - walletdb.ErasePool(nIndex); - setKeyPool.clear(); - - if (IsLocked()) - return false; - - int64 nKeys = max(GetArg("-keypool", 100), (int64)0); - for (int i = 0; i < nKeys; i++) - { - int64 nIndex = i+1; - walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); - setKeyPool.insert(nIndex); - } - printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); - } - return true; -} - -bool CWallet::TopUpKeyPool() -{ - { - LOCK(cs_wallet); - - if (IsLocked()) - return false; - - CWalletDB walletdb(strWalletFile); - - // Top up key pool - unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL); - while (setKeyPool.size() < (nTargetSize + 1)) - { - int64 nEnd = 1; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; - if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) - throw runtime_error("TopUpKeyPool() : writing generated key failed"); - setKeyPool.insert(nEnd); - printf("keypool added key %"PRI64d", size=%"PRIszu"\n", nEnd, setKeyPool.size()); - } - } - return true; -} - -void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) -{ - nIndex = -1; - keypool.vchPubKey = CPubKey(); - { - LOCK(cs_wallet); - - if (!IsLocked()) - TopUpKeyPool(); - - // Get the oldest key - if(setKeyPool.empty()) - return; - - CWalletDB walletdb(strWalletFile); - - nIndex = *(setKeyPool.begin()); - setKeyPool.erase(setKeyPool.begin()); - if (!walletdb.ReadPool(nIndex, keypool)) - throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!HaveKey(keypool.vchPubKey.GetID())) - throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); - assert(keypool.vchPubKey.IsValid()); - printf("keypool reserve %"PRI64d"\n", nIndex); - } -} - -int64 CWallet::AddReserveKey(const CKeyPool& keypool) -{ - { - LOCK2(cs_main, cs_wallet); - CWalletDB walletdb(strWalletFile); - - int64 nIndex = 1 + *(--setKeyPool.end()); - if (!walletdb.WritePool(nIndex, keypool)) - throw runtime_error("AddReserveKey() : writing added key failed"); - setKeyPool.insert(nIndex); - return nIndex; - } - return -1; -} - -void CWallet::KeepKey(int64 nIndex) -{ - // Remove from key pool - if (fFileBacked) - { - CWalletDB walletdb(strWalletFile); - walletdb.ErasePool(nIndex); - } - printf("keypool keep %"PRI64d"\n", nIndex); -} - -void CWallet::ReturnKey(int64 nIndex) -{ - // Return to key pool - { - LOCK(cs_wallet); - setKeyPool.insert(nIndex); - } - printf("keypool return %"PRI64d"\n", nIndex); -} - -bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse) -{ - int64 nIndex = 0; - CKeyPool keypool; - { - LOCK(cs_wallet); - ReserveKeyFromKeyPool(nIndex, keypool); - if (nIndex == -1) - { - if (fAllowReuse && vchDefaultKey.IsValid()) - { - result = vchDefaultKey; - return true; - } - if (IsLocked()) return false; - result = GenerateNewKey(); - return true; - } - KeepKey(nIndex); - result = keypool.vchPubKey; - } - return true; -} - -int64 CWallet::GetOldestKeyPoolTime() -{ - int64 nIndex = 0; - CKeyPool keypool; - ReserveKeyFromKeyPool(nIndex, keypool); - if (nIndex == -1) - return GetTime(); - ReturnKey(nIndex); - return keypool.nTime; -} - -std::map CWallet::GetAddressBalances() -{ - map balances; - - { - LOCK(cs_wallet); - BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) - { - CWalletTx *pcoin = &walletEntry.second; - - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? 0 : 1)) - continue; - - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - { - CTxDestination addr; - if (!IsMine(pcoin->vout[i])) - continue; - if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) - continue; - - int64 n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; - - if (!balances.count(addr)) - balances[addr] = 0; - balances[addr] += n; - } - } - } - - return balances; -} - -set< set > CWallet::GetAddressGroupings() -{ - set< set > groupings; - set grouping; - - BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) - { - CWalletTx *pcoin = &walletEntry.second; - - if (pcoin->vin.size() > 0) - { - bool any_mine = false; - // group all input addresses with each other - BOOST_FOREACH(CTxIn txin, pcoin->vin) - { - CTxDestination address; - if(!IsMine(txin)) /* If this input isn't mine, ignore it */ - continue; - if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) - continue; - grouping.insert(address); - any_mine = true; - } - - // group change with input addresses - if (any_mine) - { - BOOST_FOREACH(CTxOut txout, pcoin->vout) - if (IsChange(txout)) - { - CTxDestination txoutAddr; - if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) - continue; - grouping.insert(txoutAddr); - } - } - if (grouping.size() > 0) - { - groupings.insert(grouping); - grouping.clear(); - } - } - - // group lone addrs by themselves - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (IsMine(pcoin->vout[i])) - { - CTxDestination address; - if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) - continue; - grouping.insert(address); - groupings.insert(grouping); - grouping.clear(); - } - } - - set< set* > uniqueGroupings; // a set of pointers to groups of addresses - map< CTxDestination, set* > setmap; // map addresses to the unique group containing it - BOOST_FOREACH(set grouping, groupings) - { - // make a set of all the groups hit by this new group - set< set* > hits; - map< CTxDestination, set* >::iterator it; - BOOST_FOREACH(CTxDestination address, grouping) - if ((it = setmap.find(address)) != setmap.end()) - hits.insert((*it).second); - - // merge all hit groups into a new single group and delete old groups - set* merged = new set(grouping); - BOOST_FOREACH(set* hit, hits) - { - merged->insert(hit->begin(), hit->end()); - uniqueGroupings.erase(hit); - delete hit; - } - uniqueGroupings.insert(merged); - - // update setmap - BOOST_FOREACH(CTxDestination element, *merged) - setmap[element] = merged; - } - - set< set > ret; - BOOST_FOREACH(set* uniqueGrouping, uniqueGroupings) - { - ret.insert(*uniqueGrouping); - delete uniqueGrouping; - } - - return ret; -} - -bool CReserveKey::GetReservedKey(CPubKey& pubkey) -{ - if (nIndex == -1) - { - CKeyPool keypool; - pwallet->ReserveKeyFromKeyPool(nIndex, keypool); - if (nIndex != -1) - vchPubKey = keypool.vchPubKey; - else { - if (pwallet->vchDefaultKey.IsValid()) { - printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); - vchPubKey = pwallet->vchDefaultKey; - } else - return false; - } - } - assert(vchPubKey.IsValid()); - pubkey = vchPubKey; - return true; -} - -void CReserveKey::KeepKey() -{ - if (nIndex != -1) - pwallet->KeepKey(nIndex); - nIndex = -1; - vchPubKey = CPubKey(); -} - -void CReserveKey::ReturnKey() -{ - if (nIndex != -1) - pwallet->ReturnKey(nIndex); - nIndex = -1; - vchPubKey = CPubKey(); -} - -void CWallet::GetAllReserveKeys(set& setAddress) -{ - setAddress.clear(); - - CWalletDB walletdb(strWalletFile); - - LOCK2(cs_main, cs_wallet); - BOOST_FOREACH(const int64& id, setKeyPool) - { - CKeyPool keypool; - if (!walletdb.ReadPool(id, keypool)) - throw runtime_error("GetAllReserveKeyHashes() : read failed"); - assert(keypool.vchPubKey.IsValid()); - CKeyID keyID = keypool.vchPubKey.GetID(); - if (!HaveKey(keyID)) - throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); - setAddress.insert(keyID); - } -} - -void CWallet::UpdatedTransaction(const uint256 &hashTx) -{ - { - LOCK(cs_wallet); - // Only notify UI if this transaction is in this wallet - map::const_iterator mi = mapWallet.find(hashTx); - if (mi != mapWallet.end()) - NotifyTransactionChanged(this, hashTx, CT_UPDATED); - } -} - -void CWallet::LockCoin(COutPoint& output) -{ - setLockedCoins.insert(output); -} - -void CWallet::UnlockCoin(COutPoint& output) -{ - setLockedCoins.erase(output); -} - -void CWallet::UnlockAllCoins() -{ - setLockedCoins.clear(); -} - -bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const -{ - COutPoint outpt(hash, n); - - return (setLockedCoins.count(outpt) > 0); -} - -void CWallet::ListLockedCoins(std::vector& vOutpts) -{ - for (std::set::iterator it = setLockedCoins.begin(); - it != setLockedCoins.end(); it++) { - COutPoint outpt = (*it); - vOutpts.push_back(outpt); - } -} - diff --git a/src/wallet.h b/src/wallet.h deleted file mode 100644 index dcba4675..00000000 --- a/src/wallet.h +++ /dev/null @@ -1,862 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLET_H -#define BITCOIN_WALLET_H - -#include -#include - -#include - -#include "main.h" -#include "key.h" -#include "keystore.h" -#include "script.h" -#include "ui_interface.h" -#include "util.h" -#include "walletdb.h" - -class CAccountingEntry; -class CWalletTx; -class CReserveKey; -class COutput; - -/** (client) version numbers for particular wallet features */ -enum WalletFeature -{ - FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) - - FEATURE_WALLETCRYPT = 40000, // wallet encryption - FEATURE_COMPRPUBKEY = 60000, // compressed public keys - - FEATURE_LATEST = 60000 -}; - - -/** A key pool entry */ -class CKeyPool -{ -public: - int64 nTime; - CPubKey vchPubKey; - - CKeyPool() - { - nTime = GetTime(); - } - - CKeyPool(const CPubKey& vchPubKeyIn) - { - nTime = GetTime(); - vchPubKey = vchPubKeyIn; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(nTime); - READWRITE(vchPubKey); - ) -}; - -/** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, - * and provides the ability to create new transactions. - */ -class CWallet : public CCryptoKeyStore -{ -private: - bool SelectCoins(int64 nTargetValue, std::set >& setCoinsRet, int64& nValueRet) const; - - CWalletDB *pwalletdbEncryption; - - // the current wallet version: clients below this version are not able to load the wallet - int nWalletVersion; - - // the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded - int nWalletMaxVersion; - -public: - mutable CCriticalSection cs_wallet; - - bool fFileBacked; - std::string strWalletFile; - - std::set setKeyPool; - - - typedef std::map MasterKeyMap; - MasterKeyMap mapMasterKeys; - unsigned int nMasterKeyMaxID; - - CWallet() - { - nWalletVersion = FEATURE_BASE; - nWalletMaxVersion = FEATURE_BASE; - fFileBacked = false; - nMasterKeyMaxID = 0; - pwalletdbEncryption = NULL; - nOrderPosNext = 0; - } - CWallet(std::string strWalletFileIn) - { - nWalletVersion = FEATURE_BASE; - nWalletMaxVersion = FEATURE_BASE; - strWalletFile = strWalletFileIn; - fFileBacked = true; - nMasterKeyMaxID = 0; - pwalletdbEncryption = NULL; - nOrderPosNext = 0; - } - - std::map mapWallet; - int64 nOrderPosNext; - std::map mapRequestCount; - - std::map mapAddressBook; - - CPubKey vchDefaultKey; - - std::set setLockedCoins; - - // check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } - - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true) const; - bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64& nValueRet) const; - bool IsLockedCoin(uint256 hash, unsigned int n) const; - void LockCoin(COutPoint& output); - void UnlockCoin(COutPoint& output); - void UnlockAllCoins(); - void ListLockedCoins(std::vector& vOutpts); - - // keystore implementation - // Generate a new key - CPubKey GenerateNewKey(); - // Adds a key to the store, and saves it to disk. - bool AddKey(const CKey& key); - // Adds a key to the store, without saving it to disk (used by LoadWallet) - bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } - - bool LoadMinVersion(int nVersion) { nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } - - // Adds an encrypted key to the store, and saves it to disk. - bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); - // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) - bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); - bool AddCScript(const CScript& redeemScript); - bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); } - - bool Unlock(const SecureString& strWalletPassphrase); - bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); - bool EncryptWallet(const SecureString& strWalletPassphrase); - - /** Increment the next transaction order id - @return next transaction order id - */ - int64 IncOrderPosNext(CWalletDB *pwalletdb = NULL); - - typedef std::pair TxPair; - typedef std::multimap TxItems; - - /** Get the wallet's activity log - @return multimap of ordered transactions and accounting entries - @warning Returned pointers are *only* valid within the scope of passed acentries - */ - TxItems OrderedTxItems(std::list& acentries, std::string strAccount = ""); - - void MarkDirty(); - bool AddToWallet(const CWalletTx& wtxIn); - bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); - bool EraseFromWallet(uint256 hash); - void WalletUpdateSpent(const CTransaction& prevout); - int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); - void ReacceptWalletTransactions(); - void ResendWalletTransactions(); - int64 GetBalance() const; - int64 GetUnconfirmedBalance() const; - int64 GetImmatureBalance() const; - bool CreateTransaction(const std::vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason); - bool CreateTransaction(CScript scriptPubKey, int64 nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason); - bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); - std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); - - bool NewKeyPool(); - bool TopUpKeyPool(); - int64 AddReserveKey(const CKeyPool& keypool); - void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); - void KeepKey(int64 nIndex); - void ReturnKey(int64 nIndex); - bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true); - int64 GetOldestKeyPoolTime(); - void GetAllReserveKeys(std::set& setAddress); - - std::set< std::set > GetAddressGroupings(); - std::map GetAddressBalances(); - - bool IsMine(const CTxIn& txin) const; - int64 GetDebit(const CTxIn& txin) const; - bool IsMine(const CTxOut& txout) const - { - return ::IsMine(*this, txout.scriptPubKey); - } - int64 GetCredit(const CTxOut& txout) const - { - if (!MoneyRange(txout.nValue)) - throw std::runtime_error("CWallet::GetCredit() : value out of range"); - return (IsMine(txout) ? txout.nValue : 0); - } - bool IsChange(const CTxOut& txout) const; - int64 GetChange(const CTxOut& txout) const - { - if (!MoneyRange(txout.nValue)) - throw std::runtime_error("CWallet::GetChange() : value out of range"); - return (IsChange(txout) ? txout.nValue : 0); - } - bool IsMine(const CTransaction& tx) const - { - BOOST_FOREACH(const CTxOut& txout, tx.vout) - if (IsMine(txout)) - return true; - return false; - } - bool IsFromMe(const CTransaction& tx) const - { - return (GetDebit(tx) > 0); - } - int64 GetDebit(const CTransaction& tx) const - { - int64 nDebit = 0; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - nDebit += GetDebit(txin); - if (!MoneyRange(nDebit)) - throw std::runtime_error("CWallet::GetDebit() : value out of range"); - } - return nDebit; - } - int64 GetCredit(const CTransaction& tx) const - { - int64 nCredit = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - nCredit += GetCredit(txout); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWallet::GetCredit() : value out of range"); - } - return nCredit; - } - int64 GetChange(const CTransaction& tx) const - { - int64 nChange = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - nChange += GetChange(txout); - if (!MoneyRange(nChange)) - throw std::runtime_error("CWallet::GetChange() : value out of range"); - } - return nChange; - } - void SetBestChain(const CBlockLocator& loc); - - DBErrors LoadWallet(bool& fFirstRunRet); - - bool SetAddressBookName(const CTxDestination& address, const std::string& strName); - - bool DelAddressBookName(const CTxDestination& address); - - void UpdatedTransaction(const uint256 &hashTx); - - void PrintWallet(const CBlock& block); - - void Inventory(const uint256 &hash) - { - { - LOCK(cs_wallet); - std::map::iterator mi = mapRequestCount.find(hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } - } - - int GetKeyPoolSize() - { - return setKeyPool.size(); - } - - bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); - - bool SetDefaultKey(const CPubKey &vchPubKey); - - // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower - bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); - - // change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) - bool SetMaxVersion(int nVersion); - - // get the current wallet format (the oldest client version guaranteed to understand this wallet) - int GetVersion() { return nWalletVersion; } - - /** Address book entry changed. - * @note called with lock cs_wallet held. - */ - boost::signals2::signal NotifyAddressBookChanged; - - /** Wallet transaction added, removed or updated. - * @note called with lock cs_wallet held. - */ - boost::signals2::signal NotifyTransactionChanged; -}; - -/** A key allocated from the key pool. */ -class CReserveKey -{ -protected: - CWallet* pwallet; - int64 nIndex; - CPubKey vchPubKey; -public: - CReserveKey(CWallet* pwalletIn) - { - nIndex = -1; - pwallet = pwalletIn; - } - - ~CReserveKey() - { - ReturnKey(); - } - - void ReturnKey(); - bool GetReservedKey(CPubKey &pubkey); - void KeepKey(); -}; - - -typedef std::map mapValue_t; - - -static void ReadOrderPos(int64& nOrderPos, mapValue_t& mapValue) -{ - if (!mapValue.count("n")) - { - nOrderPos = -1; // TODO: calculate elsewhere - return; - } - nOrderPos = atoi64(mapValue["n"].c_str()); -} - - -static void WriteOrderPos(const int64& nOrderPos, mapValue_t& mapValue) -{ - if (nOrderPos == -1) - return; - mapValue["n"] = i64tostr(nOrderPos); -} - - -/** A transaction with a bunch of additional info that only the owner cares about. - * It includes any unrecorded transactions needed to link it back to the block chain. - */ -class CWalletTx : public CMerkleTx -{ -private: - const CWallet* pwallet; - -public: - std::vector vtxPrev; - mapValue_t mapValue; - std::vector > vOrderForm; - unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; // time received by this node - unsigned int nTimeSmart; - char fFromMe; - std::string strFromAccount; - std::vector vfSpent; // which outputs are already spent - int64 nOrderPos; // position in ordered transaction list - - // memory only - mutable bool fDebitCached; - mutable bool fCreditCached; - mutable bool fImmatureCreditCached; - mutable bool fAvailableCreditCached; - mutable bool fChangeCached; - mutable int64 nDebitCached; - mutable int64 nCreditCached; - mutable int64 nImmatureCreditCached; - mutable int64 nAvailableCreditCached; - mutable int64 nChangeCached; - - CWalletTx() - { - Init(NULL); - } - - CWalletTx(const CWallet* pwalletIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - void Init(const CWallet* pwalletIn) - { - pwallet = pwalletIn; - vtxPrev.clear(); - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - nTimeSmart = 0; - fFromMe = false; - strFromAccount.clear(); - vfSpent.clear(); - fDebitCached = false; - fCreditCached = false; - fImmatureCreditCached = false; - fAvailableCreditCached = false; - fChangeCached = false; - nDebitCached = 0; - nCreditCached = 0; - nImmatureCreditCached = 0; - nAvailableCreditCached = 0; - nChangeCached = 0; - nOrderPos = -1; - } - - IMPLEMENT_SERIALIZE - ( - CWalletTx* pthis = const_cast(this); - if (fRead) - pthis->Init(NULL); - char fSpent = false; - - if (!fRead) - { - pthis->mapValue["fromaccount"] = pthis->strFromAccount; - - std::string str; - BOOST_FOREACH(char f, vfSpent) - { - str += (f ? '1' : '0'); - if (f) - fSpent = true; - } - pthis->mapValue["spent"] = str; - - WriteOrderPos(pthis->nOrderPos, pthis->mapValue); - - if (nTimeSmart) - pthis->mapValue["timesmart"] = strprintf("%u", nTimeSmart); - } - - nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); - READWRITE(vtxPrev); - READWRITE(mapValue); - READWRITE(vOrderForm); - READWRITE(fTimeReceivedIsTxTime); - READWRITE(nTimeReceived); - READWRITE(fFromMe); - READWRITE(fSpent); - - if (fRead) - { - pthis->strFromAccount = pthis->mapValue["fromaccount"]; - - if (mapValue.count("spent")) - BOOST_FOREACH(char c, pthis->mapValue["spent"]) - pthis->vfSpent.push_back(c != '0'); - else - pthis->vfSpent.assign(vout.size(), fSpent); - - ReadOrderPos(pthis->nOrderPos, pthis->mapValue); - - pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0; - } - - pthis->mapValue.erase("fromaccount"); - pthis->mapValue.erase("version"); - pthis->mapValue.erase("spent"); - pthis->mapValue.erase("n"); - pthis->mapValue.erase("timesmart"); - ) - - // marks certain txout's as spent - // returns true if any update took place - bool UpdateSpent(const std::vector& vfNewSpent) - { - bool fReturn = false; - for (unsigned int i = 0; i < vfNewSpent.size(); i++) - { - if (i == vfSpent.size()) - break; - - if (vfNewSpent[i] && !vfSpent[i]) - { - vfSpent[i] = true; - fReturn = true; - fAvailableCreditCached = false; - } - } - return fReturn; - } - - // make sure balances are recalculated - void MarkDirty() - { - fCreditCached = false; - fAvailableCreditCached = false; - fDebitCached = false; - fChangeCached = false; - } - - void BindWallet(CWallet *pwalletIn) - { - pwallet = pwalletIn; - MarkDirty(); - } - - void MarkSpent(unsigned int nOut) - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); - vfSpent.resize(vout.size()); - if (!vfSpent[nOut]) - { - vfSpent[nOut] = true; - fAvailableCreditCached = false; - } - } - - bool IsSpent(unsigned int nOut) const - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); - if (nOut >= vfSpent.size()) - return false; - return (!!vfSpent[nOut]); - } - - int64 GetDebit() const - { - if (vin.empty()) - return 0; - if (fDebitCached) - return nDebitCached; - nDebitCached = pwallet->GetDebit(*this); - fDebitCached = true; - return nDebitCached; - } - - int64 GetCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - // GetBalance can assume transactions in mapWallet won't change - if (fUseCache && fCreditCached) - return nCreditCached; - nCreditCached = pwallet->GetCredit(*this); - fCreditCached = true; - return nCreditCached; - } - - int64 GetImmatureCredit(bool fUseCache=true) const - { - if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) - { - if (fUseCache && fImmatureCreditCached) - return nImmatureCreditCached; - nImmatureCreditCached = pwallet->GetCredit(*this); - fImmatureCreditCached = true; - return nImmatureCreditCached; - } - - return 0; - } - - int64 GetAvailableCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableCreditCached) - return nAvailableCreditCached; - - int64 nCredit = 0; - for (unsigned int i = 0; i < vout.size(); i++) - { - if (!IsSpent(i)) - { - const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); - } - } - - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; - return nCredit; - } - - - int64 GetChange() const - { - if (fChangeCached) - return nChangeCached; - nChangeCached = pwallet->GetChange(*this); - fChangeCached = true; - return nChangeCached; - } - - void GetAmounts(std::list >& listReceived, - std::list >& listSent, int64& nFee, std::string& strSentAccount) const; - - void GetAccountAmounts(const std::string& strAccount, int64& nReceived, - int64& nSent, int64& nFee) const; - - bool IsFromMe() const - { - return (GetDebit() > 0); - } - - bool IsConfirmed() const - { - // Quick answer in most cases - if (!IsFinal()) - return false; - if (GetDepthInMainChain() >= 1) - return true; - if (!IsFromMe()) // using wtx's cached debit - return false; - - // If no confirmations but it's from us, we can still - // consider it confirmed if all dependencies are confirmed - std::map mapPrev; - std::vector vWorkQueue; - vWorkQueue.reserve(vtxPrev.size()+1); - vWorkQueue.push_back(this); - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - const CMerkleTx* ptx = vWorkQueue[i]; - - if (!ptx->IsFinal()) - return false; - if (ptx->GetDepthInMainChain() >= 1) - continue; - if (!pwallet->IsFromMe(*ptx)) - return false; - - if (mapPrev.empty()) - { - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - mapPrev[tx.GetHash()] = &tx; - } - - BOOST_FOREACH(const CTxIn& txin, ptx->vin) - { - if (!mapPrev.count(txin.prevout.hash)) - return false; - vWorkQueue.push_back(mapPrev[txin.prevout.hash]); - } - } - return true; - } - - bool WriteToDisk(); - - int64 GetTxTime() const; - int GetRequestCount() const; - - void AddSupportingTransactions(); - bool AcceptWalletTransaction(bool fCheckInputs=true); - void RelayWalletTransaction(); -}; - - - - -class COutput -{ -public: - const CWalletTx *tx; - int i; - int nDepth; - - COutput(const CWalletTx *txIn, int iIn, int nDepthIn) - { - tx = txIn; i = iIn; nDepth = nDepthIn; - } - - std::string ToString() const - { - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString().c_str(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -/** Private key that includes an expiration date in case it never gets used. */ -class CWalletKey -{ -public: - CPrivKey vchPrivKey; - int64 nTimeCreated; - int64 nTimeExpires; - std::string strComment; - //// todo: add something to note what created it (user, getnewaddress, change) - //// maybe should have a map property map - - CWalletKey(int64 nExpires=0) - { - nTimeCreated = (nExpires ? GetTime() : 0); - nTimeExpires = nExpires; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPrivKey); - READWRITE(nTimeCreated); - READWRITE(nTimeExpires); - READWRITE(strComment); - ) -}; - - - - - - -/** Account information. - * Stored in wallet with key "acc"+string account name. - */ -class CAccount -{ -public: - CPubKey vchPubKey; - - CAccount() - { - SetNull(); - } - - void SetNull() - { - vchPubKey = CPubKey(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPubKey); - ) -}; - - - -/** Internal transfers. - * Database key is acentry. - */ -class CAccountingEntry -{ -public: - std::string strAccount; - int64 nCreditDebit; - int64 nTime; - std::string strOtherAccount; - std::string strComment; - mapValue_t mapValue; - int64 nOrderPos; // position in ordered transaction list - uint64 nEntryNo; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - nOrderPos = -1; - } - - IMPLEMENT_SERIALIZE - ( - CAccountingEntry& me = *const_cast(this); - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - // Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(strOtherAccount); - - if (!fRead) - { - WriteOrderPos(nOrderPos, me.mapValue); - - if (!(mapValue.empty() && _ssExtra.empty())) - { - CDataStream ss(nType, nVersion); - ss.insert(ss.begin(), '\0'); - ss << mapValue; - ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); - me.strComment.append(ss.str()); - } - } - - READWRITE(strComment); - - size_t nSepPos = strComment.find("\0", 0, 1); - if (fRead) - { - me.mapValue.clear(); - if (std::string::npos != nSepPos) - { - CDataStream ss(std::vector(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); - ss >> me.mapValue; - me._ssExtra = std::vector(ss.begin(), ss.end()); - } - ReadOrderPos(me.nOrderPos, me.mapValue); - } - if (std::string::npos != nSepPos) - me.strComment.erase(nSepPos); - - me.mapValue.erase("n"); - ) - -private: - std::vector _ssExtra; -}; - -bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); - -#endif diff --git a/src/walletdb.cpp b/src/walletdb.cpp deleted file mode 100644 index 9aa00e05..00000000 --- a/src/walletdb.cpp +++ /dev/null @@ -1,679 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2013 The Primecoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "walletdb.h" -#include "wallet.h" -#include - -using namespace std; -using namespace boost; - - -static uint64 nAccountingEntryNumber = 0; - -// -// CWalletDB -// - -bool CWalletDB::WriteName(const string& strAddress, const string& strName) -{ - nWalletDBUpdated++; - return Write(make_pair(string("name"), strAddress), strName); -} - -bool CWalletDB::EraseName(const string& strAddress) -{ - // This should only be used for sending addresses, never for receiving addresses, - // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdated++; - return Erase(make_pair(string("name"), strAddress)); -} - -bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) -{ - account.SetNull(); - return Read(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) -{ - return Write(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry) -{ - return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry); -} - -bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) -{ - return WriteAccountingEntry(++nAccountingEntryNumber, acentry); -} - -int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) -{ - list entries; - ListAccountCreditDebit(strAccount, entries); - - int64 nCreditDebit = 0; - BOOST_FOREACH (const CAccountingEntry& entry, entries) - nCreditDebit += entry.nCreditDebit; - - return nCreditDebit; -} - -void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) -{ - bool fAllAccounts = (strAccount == "*"); - - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); - unsigned int fFlags = DB_SET_RANGE; - loop - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (fFlags == DB_SET_RANGE) - ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); - } - - // Unserialize - string strType; - ssKey >> strType; - if (strType != "acentry") - break; - CAccountingEntry acentry; - ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) - break; - - ssValue >> acentry; - ssKey >> acentry.nEntryNo; - entries.push_back(acentry); - } - - pcursor->close(); -} - - -DBErrors -CWalletDB::ReorderTransactions(CWallet* pwallet) -{ - LOCK(pwallet->cs_wallet); - // Old wallets didn't have any defined order for transactions - // Probably a bad idea to change the output of this - - // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; - - for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); - } - list acentries; - ListAccountCreditDebit("", acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } - - int64& nOrderPosNext = pwallet->nOrderPosNext; - nOrderPosNext = 0; - std::vector nOrderPosOffsets; - for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - CAccountingEntry *const pacentry = (*it).second.second; - int64& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; - - if (nOrderPos == -1) - { - nOrderPos = nOrderPosNext++; - nOrderPosOffsets.push_back(nOrderPos); - - if (pacentry) - // Have to write accounting regardless, since we don't keep it in memory - if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DB_LOAD_FAIL; - } - else - { - int64 nOrderPosOff = 0; - BOOST_FOREACH(const int64& nOffsetStart, nOrderPosOffsets) - { - if (nOrderPos >= nOffsetStart) - ++nOrderPosOff; - } - nOrderPos += nOrderPosOff; - nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); - - if (!nOrderPosOff) - continue; - - // Since we're changing the order, write it back - if (pwtx) - { - if (!WriteTx(pwtx->GetHash(), *pwtx)) - return DB_LOAD_FAIL; - } - else - if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DB_LOAD_FAIL; - } - } - - return DB_LOAD_OK; -} - - -bool -ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, - int& nFileVersion, vector& vWalletUpgrade, - bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr) -{ - try { - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - ssKey >> strType; - if (strType == "name") - { - string strAddress; - ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; - } - else if (strType == "tx") - { - uint256 hash; - ssKey >> hash; - CWalletTx& wtx = pwallet->mapWallet[hash]; - ssValue >> wtx; - CValidationState state; - if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid()) - wtx.BindWallet(pwallet); - else - { - pwallet->mapWallet.erase(hash); - return false; - } - - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", - wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = 0; - } - vWalletUpgrade.push_back(hash); - } - - if (wtx.nOrderPos == -1) - fAnyUnordered = true; - - //// debug print - //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12"PRI64d" %s %s %s\n", - // wtx.vout[0].nValue, - // DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()).c_str(), - // wtx.hashBlock.ToString().c_str(), - // wtx.mapValue["message"].c_str()); - } - else if (strType == "acentry") - { - string strAccount; - ssKey >> strAccount; - uint64 nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; - - if (!fAnyUnordered) - { - CAccountingEntry acentry; - ssValue >> acentry; - if (acentry.nOrderPos == -1) - fAnyUnordered = true; - } - } - else if (strType == "key" || strType == "wkey") - { - vector vchPubKey; - ssKey >> vchPubKey; - CKey key; - if (strType == "key") - { - CPrivKey pkey; - ssValue >> pkey; - key.SetPubKey(vchPubKey); - if (!key.SetPrivKey(pkey)) - { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; - } - if (key.GetPubKey() != vchPubKey) - { - strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; - return false; - } - if (!key.IsValid()) - { - strErr = "Error reading wallet database: invalid CPrivKey"; - return false; - } - } - else - { - CWalletKey wkey; - ssValue >> wkey; - key.SetPubKey(vchPubKey); - if (!key.SetPrivKey(wkey.vchPrivKey)) - { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; - } - if (key.GetPubKey() != vchPubKey) - { - strErr = "Error reading wallet database: CWalletKey pubkey inconsistency"; - return false; - } - if (!key.IsValid()) - { - strErr = "Error reading wallet database: invalid CWalletKey"; - return false; - } - } - if (!pwallet->LoadKey(key)) - { - strErr = "Error reading wallet database: LoadKey failed"; - return false; - } - } - else if (strType == "mkey") - { - unsigned int nID; - ssKey >> nID; - CMasterKey kMasterKey; - ssValue >> kMasterKey; - if(pwallet->mapMasterKeys.count(nID) != 0) - { - strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); - return false; - } - pwallet->mapMasterKeys[nID] = kMasterKey; - if (pwallet->nMasterKeyMaxID < nID) - pwallet->nMasterKeyMaxID = nID; - } - else if (strType == "ckey") - { - vector vchPubKey; - ssKey >> vchPubKey; - vector vchPrivKey; - ssValue >> vchPrivKey; - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) - { - strErr = "Error reading wallet database: LoadCryptedKey failed"; - return false; - } - fIsEncrypted = true; - } - else if (strType == "defaultkey") - { - ssValue >> pwallet->vchDefaultKey; - } - else if (strType == "pool") - { - int64 nIndex; - ssKey >> nIndex; - pwallet->setKeyPool.insert(nIndex); - } - else if (strType == "version") - { - ssValue >> nFileVersion; - if (nFileVersion == 10300) - nFileVersion = 300; - } - else if (strType == "cscript") - { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->LoadCScript(script)) - { - strErr = "Error reading wallet database: LoadCScript failed"; - return false; - } - } - else if (strType == "orderposnext") - { - ssValue >> pwallet->nOrderPosNext; - } - } catch (...) - { - return false; - } - return true; -} - -static bool IsKeyType(string strType) -{ - return (strType== "key" || strType == "wkey" || - strType == "mkey" || strType == "ckey"); -} - -DBErrors CWalletDB::LoadWallet(CWallet* pwallet) -{ - pwallet->vchDefaultKey = CPubKey(); - int nFileVersion = 0; - vector vWalletUpgrade; - bool fIsEncrypted = false; - bool fAnyUnordered = false; - bool fNoncriticalErrors = false; - DBErrors result = DB_LOAD_OK; - - try { - LOCK(pwallet->cs_wallet); - int nMinVersion = 0; - if (Read((string)"minversion", nMinVersion)) - { - if (nMinVersion > CLIENT_VERSION) - return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } - - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - { - printf("Error getting wallet database cursor\n"); - return DB_CORRUPT; - } - - loop - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - printf("Error reading next record from wallet database\n"); - return DB_CORRUPT; - } - - // Try to be tolerant of single corrupt records: - string strType, strErr; - if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion, - vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr)) - { - // losing keys is considered a catastrophic error, anything else - // we assume the user can live with: - if (IsKeyType(strType)) - result = DB_CORRUPT; - else - { - // Leave other errors alone, if we try to fix them we might make things worse. - fNoncriticalErrors = true; // ... but do warn the user there is something wrong. - if (strType == "tx") - // Rescan if there is a bad transaction record: - SoftSetBoolArg("-rescan", true); - } - } - if (!strErr.empty()) - printf("%s\n", strErr.c_str()); - } - pcursor->close(); - } - catch (boost::thread_interrupted) { - throw; - } - catch (...) { - result = DB_CORRUPT; - } - - if (fNoncriticalErrors && result == DB_LOAD_OK) - result = DB_NONCRITICAL_ERROR; - - // Any wallet corruption at all: skip any rewriting or - // upgrading, we don't want to make it worse. - if (result != DB_LOAD_OK) - return result; - - printf("nFileVersion = %d\n", nFileVersion); - - BOOST_FOREACH(uint256 hash, vWalletUpgrade) - WriteTx(hash, pwallet->mapWallet[hash]); - - // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) - return DB_NEED_REWRITE; - - if (nFileVersion < CLIENT_VERSION) // Update - WriteVersion(CLIENT_VERSION); - - if (fAnyUnordered) - result = ReorderTransactions(pwallet); - - return result; -} - -void ThreadFlushWalletDB(const string& strFile) -{ - // Make this thread recognisable as the wallet flushing thread - RenameThread("primecoin-wallet"); - - static bool fOneThread; - if (fOneThread) - return; - fOneThread = true; - if (!GetBoolArg("-flushwallet", true)) - return; - - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; - int64 nLastWalletUpdate = GetTime(); - while (true) - { - MilliSleep(500); - - if (nLastSeen != nWalletDBUpdated) - { - nLastSeen = nWalletDBUpdated; - nLastWalletUpdate = GetTime(); - } - - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) - { - TRY_LOCK(bitdb.cs_db,lockDb); - if (lockDb) - { - // Don't do this if any databases are in use - int nRefCount = 0; - map::iterator mi = bitdb.mapFileUseCount.begin(); - while (mi != bitdb.mapFileUseCount.end()) - { - nRefCount += (*mi).second; - mi++; - } - - if (nRefCount == 0) - { - boost::this_thread::interruption_point(); - map::iterator mi = bitdb.mapFileUseCount.find(strFile); - if (mi != bitdb.mapFileUseCount.end()) - { - printf("Flushing wallet.dat\n"); - nLastFlushed = nWalletDBUpdated; - int64 nStart = GetTimeMillis(); - - // Flush wallet.dat so it's self contained - bitdb.CloseDb(strFile); - bitdb.CheckpointLSN(strFile); - - bitdb.mapFileUseCount.erase(mi++); - printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); - } - } - } - } - } -} - -bool BackupWallet(const CWallet& wallet, const string& strDest) -{ - if (!wallet.fFileBacked) - return false; - while (true) - { - { - LOCK(bitdb.cs_db); - if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) - { - // Flush log data to the dat file - bitdb.CloseDb(wallet.strWalletFile); - bitdb.CheckpointLSN(wallet.strWalletFile); - bitdb.mapFileUseCount.erase(wallet.strWalletFile); - - // Copy wallet.dat - filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; - filesystem::path pathDest(strDest); - if (filesystem::is_directory(pathDest)) - pathDest /= wallet.strWalletFile; - - try { -#if BOOST_VERSION >= 104000 - filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); -#else - filesystem::copy_file(pathSrc, pathDest); -#endif - printf("copied wallet.dat to %s\n", pathDest.string().c_str()); - return true; - } catch(const filesystem::filesystem_error &e) { - printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); - return false; - } - } - } - MilliSleep(100); - } - return false; -} - -// -// Try to (very carefully!) recover wallet.dat if there is a problem. -// -bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) -{ - // Recovery procedure: - // move wallet.dat to wallet.timestamp.bak - // Call Salvage with fAggressive=true to - // get as much data as possible. - // Rewrite salvaged data to wallet.dat - // Set -rescan so any missing transactions will be - // found. - int64 now = GetTime(); - std::string newFilename = strprintf("wallet.%"PRI64d".bak", now); - - int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, - newFilename.c_str(), DB_AUTO_COMMIT); - if (result == 0) - printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); - else - { - printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); - return false; - } - - std::vector salvagedData; - bool allOK = dbenv.Salvage(newFilename, true, salvagedData); - if (salvagedData.empty()) - { - printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); - return false; - } - printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size()); - - bool fSuccess = allOK; - Db* pdbCopy = new Db(&dbenv.dbenv, 0); - int ret = pdbCopy->open(NULL, // Txn pointer - filename.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - DB_CREATE, // Flags - 0); - if (ret > 0) - { - printf("Cannot create database file %s\n", filename.c_str()); - return false; - } - CWallet dummyWallet; - int nFileVersion = 0; - vector vWalletUpgrade; - bool fIsEncrypted = false; - bool fAnyUnordered = false; - - DbTxn* ptxn = dbenv.TxnBegin(); - BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) - { - if (fOnlyKeys) - { - CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); - CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); - string strType, strErr; - bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, - nFileVersion, vWalletUpgrade, - fIsEncrypted, fAnyUnordered, - strType, strErr); - if (!IsKeyType(strType)) - continue; - if (!fReadOK) - { - printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); - continue; - } - } - Dbt datKey(&row.first[0], row.first.size()); - Dbt datValue(&row.second[0], row.second.size()); - int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); - if (ret2 > 0) - fSuccess = false; - } - ptxn->commit(0); - pdbCopy->close(0); - delete pdbCopy; - - return fSuccess; -} - -bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) -{ - return CWalletDB::Recover(dbenv, filename, false); -} diff --git a/src/walletdb.h b/src/walletdb.h deleted file mode 100644 index a3e779ab..00000000 --- a/src/walletdb.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLETDB_H -#define BITCOIN_WALLETDB_H - -#include "db.h" -#include "base58.h" - -class CKeyPool; -class CAccount; -class CAccountingEntry; - -/** Error statuses for the wallet database */ -enum DBErrors -{ - DB_LOAD_OK, - DB_CORRUPT, - DB_NONCRITICAL_ERROR, - DB_TOO_NEW, - DB_LOAD_FAIL, - DB_NEED_REWRITE -}; - -/** Access to the wallet database (wallet.dat) */ -class CWalletDB : public CDB -{ -public: - CWalletDB(std::string strFilename, const char* pszMode="r+") : CDB(strFilename.c_str(), pszMode) - { - } -private: - CWalletDB(const CWalletDB&); - void operator=(const CWalletDB&); -public: - bool WriteName(const std::string& strAddress, const std::string& strName); - - bool EraseName(const std::string& strAddress); - - bool WriteTx(uint256 hash, const CWalletTx& wtx) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("tx"), hash), wtx); - } - - bool EraseTx(uint256 hash) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("tx"), hash)); - } - - bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("key"), vchPubKey.Raw()), vchPrivKey, false); - } - - bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, bool fEraseUnencryptedKey = true) - { - nWalletDBUpdated++; - if (!Write(std::make_pair(std::string("ckey"), vchPubKey.Raw()), vchCryptedSecret, false)) - return false; - if (fEraseUnencryptedKey) - { - Erase(std::make_pair(std::string("key"), vchPubKey.Raw())); - Erase(std::make_pair(std::string("wkey"), vchPubKey.Raw())); - } - return true; - } - - bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); - } - - bool WriteCScript(const uint160& hash, const CScript& redeemScript) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); - } - - bool WriteBestBlock(const CBlockLocator& locator) - { - nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); - } - - bool ReadBestBlock(CBlockLocator& locator) - { - return Read(std::string("bestblock"), locator); - } - - bool WriteOrderPosNext(int64 nOrderPosNext) - { - nWalletDBUpdated++; - return Write(std::string("orderposnext"), nOrderPosNext); - } - - bool WriteDefaultKey(const CPubKey& vchPubKey) - { - nWalletDBUpdated++; - return Write(std::string("defaultkey"), vchPubKey.Raw()); - } - - bool ReadPool(int64 nPool, CKeyPool& keypool) - { - return Read(std::make_pair(std::string("pool"), nPool), keypool); - } - - bool WritePool(int64 nPool, const CKeyPool& keypool) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("pool"), nPool), keypool); - } - - bool ErasePool(int64 nPool) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("pool"), nPool)); - } - - // Settings are no longer stored in wallet.dat; these are - // used only for backwards compatibility: - template - bool ReadSetting(const std::string& strKey, T& value) - { - return Read(std::make_pair(std::string("setting"), strKey), value); - } - template - bool WriteSetting(const std::string& strKey, const T& value) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("setting"), strKey), value); - } - bool EraseSetting(const std::string& strKey) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("setting"), strKey)); - } - - bool WriteMinVersion(int nVersion) - { - return Write(std::string("minversion"), nVersion); - } - - bool ReadAccount(const std::string& strAccount, CAccount& account); - bool WriteAccount(const std::string& strAccount, const CAccount& account); -private: - bool WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry); -public: - bool WriteAccountingEntry(const CAccountingEntry& acentry); - int64 GetAccountCreditDebit(const std::string& strAccount); - void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); - - DBErrors ReorderTransactions(CWallet*); - DBErrors LoadWallet(CWallet* pwallet); - static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); - static bool Recover(CDBEnv& dbenv, std::string filename); -}; - -#endif // BITCOIN_WALLETDB_H