diff --git a/.gitignore b/.gitignore index 54268a8..f9bb1c0 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,4 @@ config.status .vs/CCBC/v15/ipch/AutoPCH/177e9d87b59f1805/PRIVACYDIALOG.ipch .vs/CCBC/v15/ipch/AutoPCH/97cdf6ca85efe74/PROTOCOL.ipch .vs/CCBC/v15/ipch/AutoPCH/f836824dc3c97f9a/CCBC-CLI.ipch +.vs/CCBC/v15/ipch/AutoPCH/9303b40c10f48ee/PROTOCOL.ipch diff --git a/configure.ac b/configure.ac index 62a394e..e3803c7 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MINOR, 1) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 0) +define(_CLIENT_VERSION_BUILD, 1) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2018) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index be67a99..e8fc9c6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -55,9 +55,9 @@ static void convertSeed6(std::vector& vSeedsOut, const SeedSpec6* data // timestamp before) // + Contains no strange transactions static Checkpoints::MapCheckpoints mapCheckpoints = -boost::assign::map_list_of + boost::assign::map_list_of -(0, uint256("0xa9f081734c579a25872ce366d5520482755d26ff3db73fbe7bdd4e973bc0e173")); + (0, uint256("0xa9f081734c579a25872ce366d5520482755d26ff3db73fbe7bdd4e973bc0e173")); static const Checkpoints::CCheckpointData data = { &mapCheckpoints, @@ -157,6 +157,9 @@ class CMainParams : public CChainParams genesis.nBits = 504365040; genesis.nNonce = 647688; + nEnforceNewSporkKey = 1546300800; //!> Sporks signed after (GMT): Tuesday, Jan 1, 2018 12:00:00 AM GMT must use the new spork key + nRejectOldSporkKey = 1548979200; //!> Fully reject old spork key after (GMT): Friday, Feb 1, 2018 12:00:00 AM + hashGenesisBlock = genesis.GetHash(); @@ -198,8 +201,10 @@ class CMainParams : public CChainParams fHeadersFirstSyncingActive = false; nPoolMaxTransactions = 3; - //strSporkKey = "030262a2a0679d01ab026d375c42b0a4122477123631b870e09665526c322c8899"; - strSporkKey = "0257aa5bdba5b2458ac8acb5eb80277cee5c69e21a55299b5d7ac506c49d958984"; + + strSporkKey = "0257aa5bdba5b2458ac8acb5eb80277cee5c69e21a55299b5d7ac506c49d958984"; + strSporkKeyOld = "030262a2a0679d01ab026d375c42b0a4122477123631b870e09665526c322c8899"; + strObfuscationPoolDummyAddress = "XCNAsFGy8k7amqRG26ikKyfVDwK8585Z6b"; nStartMasternodePayments = 1534438799; diff --git a/src/chainparams.h b/src/chainparams.h index 261cce0..e5fc5d3 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -91,10 +91,17 @@ class CChainParams const std::vector& FixedSeeds() const { return vFixedSeeds; } virtual const Checkpoints::CCheckpointData& Checkpoints() const = 0; int PoolMaxTransactions() const { return nPoolMaxTransactions; } + + //Spork Related items + //std::string SporkKey() const { return strSporkKey; } std::string SporkKey() const { return strSporkKey; } + std::string SporkKeyOld() const { return strSporkKeyOld; } + int64_t NewSporkStart() const { return nEnforceNewSporkKey; } + int64_t RejectOldSporkKey() const { return nRejectOldSporkKey; } std::string ObfuscationPoolDummyAddress() const { return strObfuscationPoolDummyAddress; } int64_t StartMasternodePayments() const { return nStartMasternodePayments; } int64_t Budget_Fee_Confirmations() const { return nBudget_Fee_Confirmations; } + CBaseChainParams::Network NetworkID() const { return networkID; } /** Zerocoin **/ @@ -166,7 +173,13 @@ class CChainParams bool fTestnetToBeDeprecatedFieldRPC; bool fHeadersFirstSyncingActive; int nPoolMaxTransactions; + + //std::string strSporkKey; std::string strSporkKey; + std::string strSporkKeyOld; + int64_t nEnforceNewSporkKey; + int64_t nRejectOldSporkKey; + std::string strObfuscationPoolDummyAddress; int64_t nStartMasternodePayments; std::string zerocoinModulus; diff --git a/src/clientversion.h b/src/clientversion.h index fa06c95..5222017 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -18,7 +18,7 @@ #define CLIENT_VERSION_MAJOR 1 #define CLIENT_VERSION_MINOR 1 #define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 0 +#define CLIENT_VERSION_BUILD 1 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/main.cpp b/src/main.cpp index 9027c89..6fe6662 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,8 +56,8 @@ using namespace libzerocoin; // For Script size (BIGNUM/Uint256 size) #define BIGNUM_SIZE 4 /** -* Global state -*/ + * Global state + */ CCriticalSection cs_main; @@ -84,16 +84,16 @@ unsigned int nStakeMinAge = 60 * 60; // 1 Hour int64_t nReserveBalance = 0; /** Fees smaller than this (in duffs) are considered zero fee (for relaying and mining) -* We are ~100 times smaller then bitcoin now (2015-06-23), set minRelayTxFee only 10 times higher -* so it's still 10 times lower comparing to bitcoin. -*/ + * We are ~100 times smaller then bitcoin now (2015-06-23), set minRelayTxFee only 10 times higher + * so it's still 10 times lower comparing to bitcoin. + */ CFeeRate minRelayTxFee = CFeeRate(10000); CTxMemPool mempool(::minRelayTxFee); struct COrphanTx { - CTransaction tx; - NodeId fromPeer; + CTransaction tx; + NodeId fromPeer; }; map mapOrphanTransactions; map > mapOrphanTransactionsByPrev; @@ -112,144 +112,144 @@ const string strMessageMagic = "DarkNet Signed Message:\n"; // Internal stuff namespace { - struct CBlockIndexWorkComparator { - bool operator()(CBlockIndex* pa, CBlockIndex* pb) const - { - // First sort by most total work, ... - if (pa->nChainWork > pb->nChainWork) return false; - if (pa->nChainWork < pb->nChainWork) return true; - - // ... then by earliest time received, ... - if (pa->nSequenceId < pb->nSequenceId) return false; - if (pa->nSequenceId > pb->nSequenceId) return true; - - // Use pointer address as tie breaker (should only happen with blocks - // loaded from disk, as those all have id 0). - if (pa < pb) return false; - if (pa > pb) return true; - - // Identical blocks. - return false; - } - }; +struct CBlockIndexWorkComparator { + bool operator()(CBlockIndex* pa, CBlockIndex* pb) const + { + // First sort by most total work, ... + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; + + // ... then by earliest time received, ... + if (pa->nSequenceId < pb->nSequenceId) return false; + if (pa->nSequenceId > pb->nSequenceId) return true; + + // Use pointer address as tie breaker (should only happen with blocks + // loaded from disk, as those all have id 0). + if (pa < pb) return false; + if (pa > pb) return true; + + // Identical blocks. + return false; + } +}; - CBlockIndex* pindexBestInvalid; +CBlockIndex* pindexBestInvalid; - /** - * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and - * as good as our current tip or better. Entries may be failed, though. - */ - set setBlockIndexCandidates; - /** Number of nodes with fSyncStarted. */ - int nSyncStarted = 0; - /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. */ - multimap mapBlocksUnlinked; - - CCriticalSection cs_LastBlockFile; - std::vector vinfoBlockFile; - int nLastBlockFile = 0; - - /** - * Every received block is assigned a unique and increasing identifier, so we - * know which one to give priority in case of a fork. - */ - CCriticalSection cs_nBlockSequenceId; - /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ - uint32_t nBlockSequenceId = 1; +/** + * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and + * as good as our current tip or better. Entries may be failed, though. + */ +set setBlockIndexCandidates; +/** Number of nodes with fSyncStarted. */ +int nSyncStarted = 0; +/** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. */ +multimap mapBlocksUnlinked; + +CCriticalSection cs_LastBlockFile; +std::vector vinfoBlockFile; +int nLastBlockFile = 0; - /** - * Sources of received blocks, to be able to send them reject messages or ban - * them, if processing happens afterwards. Protected by cs_main. - */ - map mapBlockSource; +/** + * Every received block is assigned a unique and increasing identifier, so we + * know which one to give priority in case of a fork. + */ +CCriticalSection cs_nBlockSequenceId; +/** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ +uint32_t nBlockSequenceId = 1; - /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ - struct QueuedBlock { - uint256 hash; - CBlockIndex* pindex; //! Optional. - int64_t nTime; //! Time of "getdata" request in microseconds. - int nValidatedQueuedBefore; //! Number of blocks queued with validated headers (globally) at the time this one is requested. - bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. - }; - map::iterator> > mapBlocksInFlight; +/** + * Sources of received blocks, to be able to send them reject messages or ban + * them, if processing happens afterwards. Protected by cs_main. + */ +map mapBlockSource; + +/** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ +struct QueuedBlock { + uint256 hash; + CBlockIndex* pindex; //! Optional. + int64_t nTime; //! Time of "getdata" request in microseconds. + int nValidatedQueuedBefore; //! Number of blocks queued with validated headers (globally) at the time this one is requested. + bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. +}; +map::iterator> > mapBlocksInFlight; - /** Number of blocks in flight with validated headers. */ - int nQueuedValidatedHeaders = 0; +/** Number of blocks in flight with validated headers. */ +int nQueuedValidatedHeaders = 0; - /** Number of preferable block download peers. */ - int nPreferredDownload = 0; +/** Number of preferable block download peers. */ +int nPreferredDownload = 0; - /** Dirty block index entries. */ - set setDirtyBlockIndex; +/** Dirty block index entries. */ +set setDirtyBlockIndex; - /** Dirty block file entries. */ - set setDirtyFileInfo; -} // anon namespace +/** Dirty block file entries. */ +set setDirtyFileInfo; +} // namespace - ////////////////////////////////////////////////////////////////////////////// - // - // dispatching functions - // +////////////////////////////////////////////////////////////////////////////// +// +// dispatching functions +// - // These functions dispatch to one or all registered wallets +// These functions dispatch to one or all registered wallets namespace { - struct CMainSignals { - /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ - boost::signals2::signal SyncTransaction; - /** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */ - // XX42 boost::signals2::signal EraseTransaction; - /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ - boost::signals2::signal UpdatedTransaction; - /** Notifies listeners of a new active block chain. */ - boost::signals2::signal SetBestChain; - /** Notifies listeners about an inventory item being seen on the network. */ - boost::signals2::signal Inventory; - /** Tells listeners to broadcast their data. */ - boost::signals2::signal Broadcast; - /** Notifies listeners of a block validation result */ - boost::signals2::signal BlockChecked; - } g_signals; - -} // anon namespace +struct CMainSignals { + /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ + boost::signals2::signal SyncTransaction; + /** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */ + // XX42 boost::signals2::signal EraseTransaction; + /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ + boost::signals2::signal UpdatedTransaction; + /** Notifies listeners of a new active block chain. */ + boost::signals2::signal SetBestChain; + /** Notifies listeners about an inventory item being seen on the network. */ + boost::signals2::signal Inventory; + /** Tells listeners to broadcast their data. */ + boost::signals2::signal Broadcast; + /** Notifies listeners of a block validation result */ + boost::signals2::signal BlockChecked; +} g_signals; + +} // namespace void RegisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); - // XX42 g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); - g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); + g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); + // XX42 g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); + g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); + g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); + g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); - g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - // XX42 g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); + g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); + g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); + g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); + g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); + g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); + // XX42 g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); } void UnregisterAllValidationInterfaces() { - g_signals.BlockChecked.disconnect_all_slots(); - g_signals.Broadcast.disconnect_all_slots(); - g_signals.Inventory.disconnect_all_slots(); - g_signals.SetBestChain.disconnect_all_slots(); - g_signals.UpdatedTransaction.disconnect_all_slots(); - // XX42 g_signals.EraseTransaction.disconnect_all_slots(); - g_signals.SyncTransaction.disconnect_all_slots(); + g_signals.BlockChecked.disconnect_all_slots(); + g_signals.Broadcast.disconnect_all_slots(); + g_signals.Inventory.disconnect_all_slots(); + g_signals.SetBestChain.disconnect_all_slots(); + g_signals.UpdatedTransaction.disconnect_all_slots(); + // XX42 g_signals.EraseTransaction.disconnect_all_slots(); + g_signals.SyncTransaction.disconnect_all_slots(); } void SyncWithWallets(const CTransaction& tx, const CBlock* pblock) { - g_signals.SyncTransaction(tx, pblock); + g_signals.SyncTransaction(tx, pblock); } ////////////////////////////////////////////////////////////////////////////// @@ -259,344 +259,340 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock) namespace { - struct CBlockReject { - unsigned char chRejectCode; - string strRejectReason; - uint256 hashBlock; - }; - - /** - * Maintain validation-specific state about nodes, protected by cs_main, instead - * by CNode's own locks. This simplifies asynchronous operation, where - * processing of incoming data is done after the ProcessMessage call returns, - * and we're no longer holding the node's locks. - */ - struct CNodeState { - //! The peer's address - CService address; - //! Whether we have a fully established connection. - bool fCurrentlyConnected; - //! Accumulated misbehaviour score for this peer. - int nMisbehavior; - //! Whether this peer should be disconnected and banned (unless whitelisted). - bool fShouldBan; - //! String name of this peer (debugging/logging purposes). - std::string name; - //! List of asynchronously-determined block rejections to notify this peer about. - std::vector rejects; - //! The best known block we know this peer has announced. - CBlockIndex* pindexBestKnownBlock; - //! The hash of the last unknown block this peer has announced. - uint256 hashLastUnknownBlock; - //! The last full block we both have. - CBlockIndex* pindexLastCommonBlock; - //! Whether we've started headers synchronization with this peer. - bool fSyncStarted; - //! Since when we're stalling block download progress (in microseconds), or 0. - int64_t nStallingSince; - list vBlocksInFlight; - int nBlocksInFlight; - //! Whether we consider this a preferred download peer. - bool fPreferredDownload; - - CNodeState() - { - fCurrentlyConnected = false; - nMisbehavior = 0; - fShouldBan = false; - pindexBestKnownBlock = NULL; - hashLastUnknownBlock = uint256(0); - pindexLastCommonBlock = NULL; - fSyncStarted = false; - nStallingSince = 0; - nBlocksInFlight = 0; - fPreferredDownload = false; - } - }; - - /** Map maintaining per-node state. Requires cs_main. */ - map mapNodeState; - - // Requires cs_main. - CNodeState* State(NodeId pnode) - { - map::iterator it = mapNodeState.find(pnode); - if (it == mapNodeState.end()) - return NULL; - return &it->second; - } - - int GetHeight() - { - while (true) { - TRY_LOCK(cs_main, lockMain); - if (!lockMain) { - MilliSleep(50); - continue; - } - return chainActive.Height(); - } - } - - void UpdatePreferredDownload(CNode* node, CNodeState* state) - { - nPreferredDownload -= state->fPreferredDownload; - - // Whether this node should be marked as a preferred download node. - state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; - - nPreferredDownload += state->fPreferredDownload; - } - - void InitializeNode(NodeId nodeid, const CNode* pnode) - { - LOCK(cs_main); - CNodeState& state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; - state.name = pnode->addrName; - state.address = pnode->addr; - } - - void FinalizeNode(NodeId nodeid) - { - LOCK(cs_main); - CNodeState* state = State(nodeid); - - if (state->fSyncStarted) - nSyncStarted--; - - if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - AddressCurrentlyConnected(state->address); - } - - BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) - mapBlocksInFlight.erase(entry.hash); - EraseOrphansFor(nodeid); - nPreferredDownload -= state->fPreferredDownload; - - mapNodeState.erase(nodeid); - } - - // Requires cs_main. - void MarkBlockAsReceived(const uint256& hash) - { - map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); - if (itInFlight != mapBlocksInFlight.end()) { - CNodeState* state = State(itInFlight->second.first); - nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; - state->vBlocksInFlight.erase(itInFlight->second.second); - state->nBlocksInFlight--; - state->nStallingSince = 0; - mapBlocksInFlight.erase(itInFlight); - } - } - - // Requires cs_main. - void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex* pindex = NULL) - { - CNodeState* state = State(nodeid); - assert(state != NULL); - - // Make sure it's not listed somewhere already. - MarkBlockAsReceived(hash); - - QueuedBlock newentry = { hash, pindex, GetTimeMicros(), nQueuedValidatedHeaders, pindex != NULL }; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; - list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); - state->nBlocksInFlight++; - mapBlocksInFlight[hash] = std::make_pair(nodeid, it); - } - - /** Check whether the last unknown block a peer advertized is not yet known. */ - void ProcessBlockAvailability(NodeId nodeid) - { - CNodeState* state = State(nodeid); - assert(state != NULL); - - if (state->hashLastUnknownBlock != 0) { - BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); - if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { - if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = itOld->second; - state->hashLastUnknownBlock = uint256(0); - } - } - } - - /** Update tracking information about which blocks a peer is assumed to have. */ - void UpdateBlockAvailability(NodeId nodeid, const uint256& hash) - { - CNodeState* state = State(nodeid); - assert(state != NULL); - - ProcessBlockAvailability(nodeid); - - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { - // An actually better block was announced. - if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = it->second; - } - else { - // An unknown block was announced; just assume that the latest one is the best one. - state->hashLastUnknownBlock = hash; - } - } - - /** Find the last common ancestor two blocks have. - * Both pa and pb must be non-NULL. */ - CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) - { - if (pa->nHeight > pb->nHeight) { - pa = pa->GetAncestor(pb->nHeight); - } - else if (pb->nHeight > pa->nHeight) { - pb = pb->GetAncestor(pa->nHeight); - } - - while (pa != pb && pa && pb) { - pa = pa->pprev; - pb = pb->pprev; - } - - // Eventually all chain branches meet at the genesis block. - assert(pa == pb); - return pa; - } - - /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has - * at most count entries. */ - void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) - { - if (count == 0) - return; - - vBlocks.reserve(vBlocks.size() + count); - CNodeState* state = State(nodeid); - assert(state != NULL); - - // Make sure pindexBestKnownBlock is up to date, we'll need it. - ProcessBlockAvailability(nodeid); - - if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { - // This peer has nothing interesting. - return; - } - - if (state->pindexLastCommonBlock == NULL) { - // Bootstrap quickly by guessing a parent of our best tip is the forking point. - // Guessing wrong in either direction is not a problem. - state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; - } - - // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor - // of their current tip anymore. Go back enough to fix that. - state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); - if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) - return; - - std::vector vToFetch; - CBlockIndex* pindexWalk = state->pindexLastCommonBlock; - // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last - // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to - // download that next block if the window were 1 larger. - int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; - int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); - NodeId waitingfor = -1; - while (pindexWalk->nHeight < nMaxHeight) { - // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards - // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive - // as iterating over ~100 CBlockIndex* entries anyway. - int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); - vToFetch.resize(nToFetch); - pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); - vToFetch[nToFetch - 1] = pindexWalk; - for (unsigned int i = nToFetch - 1; i > 0; i--) { - vToFetch[i - 1] = vToFetch[i]->pprev; - } - - // Iterate over those blocks in vToFetch (in forward direction), adding the ones that - // are not yet downloaded and not in flight to vBlocks. In the mean time, update - // pindexLastCommonBlock as long as all ancestors are already downloaded. - BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { - if (!pindex->IsValid(BLOCK_VALID_TREE)) { - // We consider the chain that this peer is on invalid. - return; - } - if (pindex->nStatus & BLOCK_HAVE_DATA) { - if (pindex->nChainTx) - state->pindexLastCommonBlock = pindex; - } - else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { - // The block is not already downloaded, and not yet in flight. - if (pindex->nHeight > nWindowEnd) { - // We reached the end of the window. - if (vBlocks.size() == 0 && waitingfor != nodeid) { - // We aren't able to fetch anything, but we would be if the download window was one larger. - nodeStaller = waitingfor; - } - return; - } - vBlocks.push_back(pindex); - if (vBlocks.size() == count) { - return; - } - } - else if (waitingfor == -1) { - // This is the first already-in-flight block. - waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; - } - } - } - } - -} // anon namespace +struct CBlockReject { + unsigned char chRejectCode; + string strRejectReason; + uint256 hashBlock; +}; + +/** + * Maintain validation-specific state about nodes, protected by cs_main, instead + * by CNode's own locks. This simplifies asynchronous operation, where + * processing of incoming data is done after the ProcessMessage call returns, + * and we're no longer holding the node's locks. + */ +struct CNodeState { + //! The peer's address + CService address; + //! Whether we have a fully established connection. + bool fCurrentlyConnected; + //! Accumulated misbehaviour score for this peer. + int nMisbehavior; + //! Whether this peer should be disconnected and banned (unless whitelisted). + bool fShouldBan; + //! String name of this peer (debugging/logging purposes). + std::string name; + //! List of asynchronously-determined block rejections to notify this peer about. + std::vector rejects; + //! The best known block we know this peer has announced. + CBlockIndex* pindexBestKnownBlock; + //! The hash of the last unknown block this peer has announced. + uint256 hashLastUnknownBlock; + //! The last full block we both have. + CBlockIndex* pindexLastCommonBlock; + //! Whether we've started headers synchronization with this peer. + bool fSyncStarted; + //! Since when we're stalling block download progress (in microseconds), or 0. + int64_t nStallingSince; + list vBlocksInFlight; + int nBlocksInFlight; + //! Whether we consider this a preferred download peer. + bool fPreferredDownload; + + CNodeState() + { + fCurrentlyConnected = false; + nMisbehavior = 0; + fShouldBan = false; + pindexBestKnownBlock = NULL; + hashLastUnknownBlock = uint256(0); + pindexLastCommonBlock = NULL; + fSyncStarted = false; + nStallingSince = 0; + nBlocksInFlight = 0; + fPreferredDownload = false; + } +}; + +/** Map maintaining per-node state. Requires cs_main. */ +map mapNodeState; + +// Requires cs_main. +CNodeState* State(NodeId pnode) +{ + map::iterator it = mapNodeState.find(pnode); + if (it == mapNodeState.end()) + return NULL; + return &it->second; +} + +int GetHeight() +{ + while (true) { + TRY_LOCK(cs_main, lockMain); + if (!lockMain) { + MilliSleep(50); + continue; + } + return chainActive.Height(); + } +} + +void UpdatePreferredDownload(CNode* node, CNodeState* state) +{ + nPreferredDownload -= state->fPreferredDownload; + + // Whether this node should be marked as a preferred download node. + state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; + + nPreferredDownload += state->fPreferredDownload; +} + +void InitializeNode(NodeId nodeid, const CNode* pnode) +{ + LOCK(cs_main); + CNodeState& state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; + state.name = pnode->addrName; + state.address = pnode->addr; +} + +void FinalizeNode(NodeId nodeid) +{ + LOCK(cs_main); + CNodeState* state = State(nodeid); + + if (state->fSyncStarted) + nSyncStarted--; + + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + AddressCurrentlyConnected(state->address); + } + + BOOST_FOREACH (const QueuedBlock& entry, state->vBlocksInFlight) + mapBlocksInFlight.erase(entry.hash); + EraseOrphansFor(nodeid); + nPreferredDownload -= state->fPreferredDownload; + + mapNodeState.erase(nodeid); +} + +// Requires cs_main. +void MarkBlockAsReceived(const uint256& hash) +{ + map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); + if (itInFlight != mapBlocksInFlight.end()) { + CNodeState* state = State(itInFlight->second.first); + nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; + state->vBlocksInFlight.erase(itInFlight->second.second); + state->nBlocksInFlight--; + state->nStallingSince = 0; + mapBlocksInFlight.erase(itInFlight); + } +} + +// Requires cs_main. +void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex* pindex = NULL) +{ + CNodeState* state = State(nodeid); + assert(state != NULL); + + // Make sure it's not listed somewhere already. + MarkBlockAsReceived(hash); + + QueuedBlock newentry = {hash, pindex, GetTimeMicros(), nQueuedValidatedHeaders, pindex != NULL}; + nQueuedValidatedHeaders += newentry.fValidatedHeaders; + list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); + state->nBlocksInFlight++; + mapBlocksInFlight[hash] = std::make_pair(nodeid, it); +} + +/** Check whether the last unknown block a peer advertized is not yet known. */ +void ProcessBlockAvailability(NodeId nodeid) +{ + CNodeState* state = State(nodeid); + assert(state != NULL); + + if (state->hashLastUnknownBlock != 0) { + BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); + if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { + if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = itOld->second; + state->hashLastUnknownBlock = uint256(0); + } + } +} + +/** Update tracking information about which blocks a peer is assumed to have. */ +void UpdateBlockAvailability(NodeId nodeid, const uint256& hash) +{ + CNodeState* state = State(nodeid); + assert(state != NULL); + + ProcessBlockAvailability(nodeid); + + BlockMap::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { + // An actually better block was announced. + if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = it->second; + } else { + // An unknown block was announced; just assume that the latest one is the best one. + state->hashLastUnknownBlock = hash; + } +} + +/** Find the last common ancestor two blocks have. + * Both pa and pb must be non-NULL. */ +CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) +{ + if (pa->nHeight > pb->nHeight) { + pa = pa->GetAncestor(pb->nHeight); + } else if (pb->nHeight > pa->nHeight) { + pb = pb->GetAncestor(pa->nHeight); + } + + while (pa != pb && pa && pb) { + pa = pa->pprev; + pb = pb->pprev; + } + + // Eventually all chain branches meet at the genesis block. + assert(pa == pb); + return pa; +} + +/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has + * at most count entries. */ +void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) +{ + if (count == 0) + return; + + vBlocks.reserve(vBlocks.size() + count); + CNodeState* state = State(nodeid); + assert(state != NULL); + + // Make sure pindexBestKnownBlock is up to date, we'll need it. + ProcessBlockAvailability(nodeid); + + if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { + // This peer has nothing interesting. + return; + } + + if (state->pindexLastCommonBlock == NULL) { + // Bootstrap quickly by guessing a parent of our best tip is the forking point. + // Guessing wrong in either direction is not a problem. + state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; + } + + // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor + // of their current tip anymore. Go back enough to fix that. + state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); + if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) + return; + + std::vector vToFetch; + CBlockIndex* pindexWalk = state->pindexLastCommonBlock; + // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last + // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to + // download that next block if the window were 1 larger. + int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; + int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); + NodeId waitingfor = -1; + while (pindexWalk->nHeight < nMaxHeight) { + // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards + // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive + // as iterating over ~100 CBlockIndex* entries anyway. + int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); + vToFetch.resize(nToFetch); + pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); + vToFetch[nToFetch - 1] = pindexWalk; + for (unsigned int i = nToFetch - 1; i > 0; i--) { + vToFetch[i - 1] = vToFetch[i]->pprev; + } + + // Iterate over those blocks in vToFetch (in forward direction), adding the ones that + // are not yet downloaded and not in flight to vBlocks. In the mean time, update + // pindexLastCommonBlock as long as all ancestors are already downloaded. + BOOST_FOREACH (CBlockIndex* pindex, vToFetch) { + if (!pindex->IsValid(BLOCK_VALID_TREE)) { + // We consider the chain that this peer is on invalid. + return; + } + if (pindex->nStatus & BLOCK_HAVE_DATA) { + if (pindex->nChainTx) + state->pindexLastCommonBlock = pindex; + } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { + // The block is not already downloaded, and not yet in flight. + if (pindex->nHeight > nWindowEnd) { + // We reached the end of the window. + if (vBlocks.size() == 0 && waitingfor != nodeid) { + // We aren't able to fetch anything, but we would be if the download window was one larger. + nodeStaller = waitingfor; + } + return; + } + vBlocks.push_back(pindex); + if (vBlocks.size() == count) { + return; + } + } else if (waitingfor == -1) { + // This is the first already-in-flight block. + waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; + } + } + } +} + +} // namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) { - LOCK(cs_main); - CNodeState* state = State(nodeid); - if (state == NULL) - return false; - stats.nMisbehavior = state->nMisbehavior; - stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; - stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; - BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) { - if (queue.pindex) - stats.vHeightInFlight.push_back(queue.pindex->nHeight); - } - return true; + LOCK(cs_main); + CNodeState* state = State(nodeid); + if (state == NULL) + return false; + stats.nMisbehavior = state->nMisbehavior; + stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; + stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; + BOOST_FOREACH (const QueuedBlock& queue, state->vBlocksInFlight) { + if (queue.pindex) + stats.vHeightInFlight.push_back(queue.pindex->nHeight); + } + return true; } void RegisterNodeSignals(CNodeSignals& nodeSignals) { - nodeSignals.GetHeight.connect(&GetHeight); - nodeSignals.ProcessMessages.connect(&ProcessMessages); - nodeSignals.SendMessages.connect(&SendMessages); - nodeSignals.InitializeNode.connect(&InitializeNode); - nodeSignals.FinalizeNode.connect(&FinalizeNode); + nodeSignals.GetHeight.connect(&GetHeight); + nodeSignals.ProcessMessages.connect(&ProcessMessages); + nodeSignals.SendMessages.connect(&SendMessages); + nodeSignals.InitializeNode.connect(&InitializeNode); + nodeSignals.FinalizeNode.connect(&FinalizeNode); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) { - nodeSignals.GetHeight.disconnect(&GetHeight); - nodeSignals.ProcessMessages.disconnect(&ProcessMessages); - nodeSignals.SendMessages.disconnect(&SendMessages); - nodeSignals.InitializeNode.disconnect(&InitializeNode); - nodeSignals.FinalizeNode.disconnect(&FinalizeNode); + nodeSignals.GetHeight.disconnect(&GetHeight); + nodeSignals.ProcessMessages.disconnect(&ProcessMessages); + nodeSignals.SendMessages.disconnect(&SendMessages); + nodeSignals.InitializeNode.disconnect(&InitializeNode); + nodeSignals.FinalizeNode.disconnect(&FinalizeNode); } CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, locator.vHave) { - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex* pindex = (*mi).second; - if (chain.Contains(pindex)) - return pindex; - } - } - return chain.Genesis(); + // Find the first block the caller has in the main chain + BOOST_FOREACH (const uint256& hash, locator.vHave) { + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) { + CBlockIndex* pindex = (*mi).second; + if (chain.Contains(pindex)) + return pindex; + } + } + return chain.Genesis(); } CCoinsViewCache* pcoinsTip = NULL; @@ -611,330 +607,327 @@ CSporkDB* pSporkDB = NULL; bool AddOrphanTx(const CTransaction& tx, NodeId peer) { - uint256 hash = tx.GetHash(); - if (mapOrphanTransactions.count(hash)) - return false; - - // 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: - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz > 5000) { - LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); - return false; - } - - mapOrphanTransactions[hash].tx = tx; - mapOrphanTransactions[hash].fromPeer = peer; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - - LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), - mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); - return true; + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return false; + + // 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: + unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz > 5000) { + LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); + return false; + } + + mapOrphanTransactions[hash].tx = tx; + mapOrphanTransactions[hash].fromPeer = peer; + BOOST_FOREACH (const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); + + LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), + mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); + return true; } void static EraseOrphanTx(uint256 hash) { - map::iterator it = mapOrphanTransactions.find(hash); - if (it == mapOrphanTransactions.end()) - return; - BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin) { - map >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); - if (itPrev == mapOrphanTransactionsByPrev.end()) - continue; - itPrev->second.erase(hash); - if (itPrev->second.empty()) - mapOrphanTransactionsByPrev.erase(itPrev); - } - mapOrphanTransactions.erase(it); + map::iterator it = mapOrphanTransactions.find(hash); + if (it == mapOrphanTransactions.end()) + return; + BOOST_FOREACH (const CTxIn& txin, it->second.tx.vin) { + map >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); + if (itPrev == mapOrphanTransactionsByPrev.end()) + continue; + itPrev->second.erase(hash); + if (itPrev->second.empty()) + mapOrphanTransactionsByPrev.erase(itPrev); + } + mapOrphanTransactions.erase(it); } void EraseOrphansFor(NodeId peer) { - int nErased = 0; - map::iterator iter = mapOrphanTransactions.begin(); - while (iter != mapOrphanTransactions.end()) { - map::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid - if (maybeErase->second.fromPeer == peer) { - EraseOrphanTx(maybeErase->second.tx.GetHash()); - ++nErased; - } - } - if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); + int nErased = 0; + map::iterator iter = mapOrphanTransactions.begin(); + while (iter != mapOrphanTransactions.end()) { + map::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid + if (maybeErase->second.fromPeer == peer) { + EraseOrphanTx(maybeErase->second.tx.GetHash()); + ++nErased; + } + } + if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); } 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; + 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; } bool IsStandardTx(const CTransaction& tx, string& reason) { - AssertLockHeld(cs_main); - if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { - reason = "version"; - return false; - } - - // Treat non-final transactions as non-standard to prevent a specific type - // of double-spend attack, as well as DoS attacks. (if the transaction - // can't be mined, the attacker isn't expending resources broadcasting it) - // Basically we don't want to propagate transactions that can't be included in - // the next block. - // - // However, IsFinalTx() is confusing... Without arguments, it uses - // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() - // is set to the value of nHeight in the block. However, when IsFinalTx() - // is called within CBlock::AcceptBlock(), the height of the block *being* - // evaluated is what is used. Thus if we want to know if a transaction can - // be part of the *next* block, we need to call IsFinalTx() with one more - // than chainActive.Height(). - // - // Timestamps on the other hand don't get any special treatment, because we - // can't know what timestamp the next block will have, and there aren't - // timestamp applications where it matters. - if (!IsFinalTx(tx, chainActive.Height() + 1)) { - reason = "non-final"; - 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 = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - unsigned int nMaxSize = tx.ContainsZerocoins() ? MAX_ZEROCOIN_TX_SIZE : MAX_STANDARD_TX_SIZE; - if (sz >= nMaxSize) { - reason = "tx-size"; - return false; - } - - for (const CTxIn& txin : tx.vin) { - if (txin.scriptSig.IsZerocoinSpend()) - continue; - // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed - // keys. (remember the 520 byte limit on redeemScript size) That works - // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 - // bytes of scriptSig, which we round off to 1650 bytes for some minor - // future-proofing. That's also enough to spend a 20-of-20 - // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not - // considered standard) - if (txin.scriptSig.size() > 1650) { - reason = "scriptsig-size"; - return false; - } - if (!txin.scriptSig.IsPushOnly()) { - reason = "scriptsig-not-pushonly"; - return false; - } - } - - unsigned int nDataOut = 0; - txnouttype whichType; - BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType)) { - reason = "scriptpubkey"; - return false; - } - - if (whichType == TX_NULL_DATA) - nDataOut++; - else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { - reason = "bare-multisig"; - return false; - } - else if (txout.IsDust(::minRelayTxFee)) { - reason = "dust"; - return false; - } - } - - // only one OP_RETURN txout is permitted - if (nDataOut > 1) { - reason = "multi-op-return"; - return false; - } - - return true; + AssertLockHeld(cs_main); + if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { + reason = "version"; + return false; + } + + // Treat non-final transactions as non-standard to prevent a specific type + // of double-spend attack, as well as DoS attacks. (if the transaction + // can't be mined, the attacker isn't expending resources broadcasting it) + // Basically we don't want to propagate transactions that can't be included in + // the next block. + // + // However, IsFinalTx() is confusing... Without arguments, it uses + // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() + // is set to the value of nHeight in the block. However, when IsFinalTx() + // is called within CBlock::AcceptBlock(), the height of the block *being* + // evaluated is what is used. Thus if we want to know if a transaction can + // be part of the *next* block, we need to call IsFinalTx() with one more + // than chainActive.Height(). + // + // Timestamps on the other hand don't get any special treatment, because we + // can't know what timestamp the next block will have, and there aren't + // timestamp applications where it matters. + if (!IsFinalTx(tx, chainActive.Height() + 1)) { + reason = "non-final"; + 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 = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + unsigned int nMaxSize = tx.ContainsZerocoins() ? MAX_ZEROCOIN_TX_SIZE : MAX_STANDARD_TX_SIZE; + if (sz >= nMaxSize) { + reason = "tx-size"; + return false; + } + + for (const CTxIn& txin : tx.vin) { + if (txin.scriptSig.IsZerocoinSpend()) + continue; + // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed + // keys. (remember the 520 byte limit on redeemScript size) That works + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 + // bytes of scriptSig, which we round off to 1650 bytes for some minor + // future-proofing. That's also enough to spend a 20-of-20 + // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not + // considered standard) + if (txin.scriptSig.size() > 1650) { + reason = "scriptsig-size"; + return false; + } + if (!txin.scriptSig.IsPushOnly()) { + reason = "scriptsig-not-pushonly"; + return false; + } + } + + unsigned int nDataOut = 0; + txnouttype whichType; + BOOST_FOREACH (const CTxOut& txout, tx.vout) { + if (!::IsStandard(txout.scriptPubKey, whichType)) { + reason = "scriptpubkey"; + return false; + } + + if (whichType == TX_NULL_DATA) + nDataOut++; + else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { + reason = "bare-multisig"; + return false; + } else if (txout.IsDust(::minRelayTxFee)) { + reason = "dust"; + return false; + } + } + + // only one OP_RETURN txout is permitted + if (nDataOut > 1) { + reason = "multi-op-return"; + return false; + } + + return true; } bool IsFinalTx(const CTransaction& tx, int nBlockHeight, int64_t nBlockTime) { - AssertLockHeld(cs_main); - // Time based nLockTime implemented in 0.1.6 - if (tx.nLockTime == 0) - return true; - if (nBlockHeight == 0) - nBlockHeight = chainActive.Height(); - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); - if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) - return true; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (!txin.IsFinal()) - return false; - return true; + AssertLockHeld(cs_main); + // Time based nLockTime implemented in 0.1.6 + if (tx.nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = chainActive.Height(); + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH (const CTxIn& txin, tx.vin) + if (!txin.IsFinal()) + return false; + return true; } /** -* Check transaction inputs to mitigate two -* potential denial-of-service attacks: -* -* 1. scriptSigs with extra data stuffed into them, -* not consumed by scriptPubKey (or P2SH script) -* 2. P2SH scripts with a crazy number of expensive -* CHECKSIG/CHECKMULTISIG operations -*/ + * Check transaction inputs to mitigate two + * potential denial-of-service attacks: + * + * 1. scriptSigs with extra data stuffed into them, + * not consumed by scriptPubKey (or P2SH script) + * 2. P2SH scripts with a crazy number of expensive + * CHECKSIG/CHECKMULTISIG operations + */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { - if (tx.IsCoinBase() || tx.IsZerocoinSpend()) - return true; // coinbase has no inputs and zerocoinspend has a special input - //todo should there be a check for a 'standard' zerocoinspend here? - - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); - - 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 - // IsStandard() will have already returned false - // and this method isn't called. - vector > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker())) - 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)) { - int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - else { - // Any other Script with less than 15 sigops OK: - unsigned int sigops = subscript.GetSigOpCount(true); - // ... extra data left on the stack after execution is OK, too: - return (sigops <= MAX_P2SH_SIGOPS); - } - } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; - } + if (tx.IsCoinBase() || tx.IsZerocoinSpend()) + return true; // coinbase has no inputs and zerocoinspend has a special input + //todo should there be a check for a 'standard' zerocoinspend here? + + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); + + 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 + // IsStandard() will have already returned false + // and this method isn't called. + vector > stack; + if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker())) + 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)) { + int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } else { + // Any other Script with less than 15 sigops OK: + unsigned int sigops = subscript.GetSigOpCount(true); + // ... extra data left on the stack after execution is OK, too: + return (sigops <= MAX_P2SH_SIGOPS); + } + } - return true; + if (stack.size() != (unsigned int)nArgsExpected) + return false; + } + + return true; } unsigned int GetLegacySigOpCount(const CTransaction& tx) { - unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, tx.vin) { - nSigOps += txin.scriptSig.GetSigOpCount(false); - } - BOOST_FOREACH(const CTxOut& txout, tx.vout) { - nSigOps += txout.scriptPubKey.GetSigOpCount(false); - } - return nSigOps; + unsigned int nSigOps = 0; + BOOST_FOREACH (const CTxIn& txin, tx.vin) { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + BOOST_FOREACH (const CTxOut& txout, tx.vout) { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; } unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) { - if (tx.IsCoinBase() || tx.IsZerocoinSpend()) - return 0; + if (tx.IsCoinBase() || tx.IsZerocoinSpend()) + return 0; - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut& prevout = inputs.GetOutputFor(tx.vin[i]); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); - } - return nSigOps; + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const CTxOut& prevout = inputs.GetOutputFor(tx.vin[i]); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); + } + return nSigOps; } int GetInputAge(CTxIn& vin) { - CCoinsView viewDummy; - CCoinsViewCache view(&viewDummy); - { - LOCK(mempool.cs); - CCoinsViewMemPool viewMempool(pcoinsTip, mempool); - view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + CCoinsView viewDummy; + CCoinsViewCache view(&viewDummy); + { + LOCK(mempool.cs); + CCoinsViewMemPool viewMempool(pcoinsTip, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view - const CCoins* coins = view.AccessCoins(vin.prevout.hash); + const CCoins* coins = view.AccessCoins(vin.prevout.hash); - if (coins) { - if (coins->nHeight < 0) return 0; - return (chainActive.Tip()->nHeight + 1) - coins->nHeight; - } - else - return -1; - } + if (coins) { + if (coins->nHeight < 0) return 0; + return (chainActive.Tip()->nHeight + 1) - coins->nHeight; + } else + return -1; + } } int GetInputAgeIX(uint256 nTXHash, CTxIn& vin) { - int sigs = 0; - int nResult = GetInputAge(vin); - if (nResult < 0) nResult = 0; + int sigs = 0; + int nResult = GetInputAge(vin); + if (nResult < 0) nResult = 0; - if (nResult < 6) { - std::map::iterator i = mapTxLocks.find(nTXHash); - if (i != mapTxLocks.end()) { - sigs = (*i).second.CountSignatures(); - } - if (sigs >= SWIFTTX_SIGNATURES_REQUIRED) { - return nSwiftTXDepth + nResult; - } - } + if (nResult < 6) { + std::map::iterator i = mapTxLocks.find(nTXHash); + if (i != mapTxLocks.end()) { + sigs = (*i).second.CountSignatures(); + } + if (sigs >= SWIFTTX_SIGNATURES_REQUIRED) { + return nSwiftTXDepth + nResult; + } + } - return -1; + return -1; } int GetIXConfirmations(uint256 nTXHash) { - int sigs = 0; + int sigs = 0; - std::map::iterator i = mapTxLocks.find(nTXHash); - if (i != mapTxLocks.end()) { - sigs = (*i).second.CountSignatures(); - } - if (sigs >= SWIFTTX_SIGNATURES_REQUIRED) { - return nSwiftTXDepth; - } + std::map::iterator i = mapTxLocks.find(nTXHash); + if (i != mapTxLocks.end()) { + sigs = (*i).second.CountSignatures(); + } + if (sigs >= SWIFTTX_SIGNATURES_REQUIRED) { + return nSwiftTXDepth; + } - return 0; + return 0; } // ppcoin: total coin age spent in transaction, in the unit of coin-days. @@ -946,1100 +939,1093 @@ int GetIXConfirmations(uint256 nTXHash) // age (trust score) of competing branches. bool GetCoinAge(const CTransaction& tx, const unsigned int nTxTime, uint64_t& nCoinAge) { - uint256 bnCentSecond = 0; // coin age in the unit of cent-seconds - nCoinAge = 0; - - CBlockIndex* pindex = NULL; - BOOST_FOREACH(const CTxIn& txin, tx.vin) { - // First try finding the previous transaction in database - CTransaction txPrev; - uint256 hashBlockPrev; - if (!GetTransaction(txin.prevout.hash, txPrev, hashBlockPrev, true)) { - LogPrintf("GetCoinAge: failed to find vin transaction \n"); - continue; // previous transaction not in main chain - } - - BlockMap::iterator it = mapBlockIndex.find(hashBlockPrev); - if (it != mapBlockIndex.end()) - pindex = it->second; - else { - LogPrintf("GetCoinAge() failed to find block index \n"); - continue; - } - - // Read block header - CBlockHeader prevblock = pindex->GetBlockHeader(); - - if (prevblock.nTime + nStakeMinAge > nTxTime) - continue; // only count coins meeting min age requirement - - if (nTxTime < prevblock.nTime) { - LogPrintf("GetCoinAge: Timestamp Violation: txtime less than txPrev.nTime"); - return false; // Transaction timestamp violation - } - - int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; - bnCentSecond += uint256(nValueIn) * (nTxTime - prevblock.nTime); - } - - uint256 bnCoinDay = bnCentSecond / COIN / (24 * 60 * 60); - LogPrintf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str()); - nCoinAge = bnCoinDay.GetCompact(); - return true; + uint256 bnCentSecond = 0; // coin age in the unit of cent-seconds + nCoinAge = 0; + + CBlockIndex* pindex = NULL; + BOOST_FOREACH (const CTxIn& txin, tx.vin) { + // First try finding the previous transaction in database + CTransaction txPrev; + uint256 hashBlockPrev; + if (!GetTransaction(txin.prevout.hash, txPrev, hashBlockPrev, true)) { + LogPrintf("GetCoinAge: failed to find vin transaction \n"); + continue; // previous transaction not in main chain + } + + BlockMap::iterator it = mapBlockIndex.find(hashBlockPrev); + if (it != mapBlockIndex.end()) + pindex = it->second; + else { + LogPrintf("GetCoinAge() failed to find block index \n"); + continue; + } + + // Read block header + CBlockHeader prevblock = pindex->GetBlockHeader(); + + if (prevblock.nTime + nStakeMinAge > nTxTime) + continue; // only count coins meeting min age requirement + + if (nTxTime < prevblock.nTime) { + LogPrintf("GetCoinAge: Timestamp Violation: txtime less than txPrev.nTime"); + return false; // Transaction timestamp violation + } + + int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; + bnCentSecond += uint256(nValueIn) * (nTxTime - prevblock.nTime); + } + + uint256 bnCoinDay = bnCentSecond / COIN / (24 * 60 * 60); + LogPrintf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str()); + nCoinAge = bnCoinDay.GetCompact(); + return true; } bool MoneyRange(CAmount nValueOut) { - return nValueOut >= 0 && nValueOut <= Params().MaxMoneyOut(); + return nValueOut >= 0 && nValueOut <= Params().MaxMoneyOut(); } int GetZerocoinStartHeight() { - return Params().Zerocoin_StartHeight(); + return Params().Zerocoin_StartHeight(); } void FindMints(vector vMintsToFind, vector& vMintsToUpdate, vector& vMissingMints, bool fExtendedSearch) { - // see which mints are in our public zerocoin database. The mint should be here if it exists, unless - // something went wrong - for (CZerocoinMint mint : vMintsToFind) { - uint256 txHash; - if (!zerocoinDB->ReadCoinMint(mint.GetValue(), txHash)) { - vMissingMints.push_back(mint); - continue; - } - - // make sure the txhash and block height meta data are correct for this mint - CTransaction tx; - uint256 hashBlock; - if (!GetTransaction(txHash, tx, hashBlock, true)) { - LogPrintf("%s : cannot find tx %s\n", __func__, txHash.GetHex()); - vMissingMints.push_back(mint); - continue; - } - - if (!mapBlockIndex.count(hashBlock)) { - LogPrintf("%s : cannot find block %s\n", __func__, hashBlock.GetHex()); - vMissingMints.push_back(mint); - continue; - } - - //see if this mint is spent - uint256 hashTxSpend = 0; - zerocoinDB->ReadCoinSpend(mint.GetSerialNumber(), hashTxSpend); - bool fSpent = hashTxSpend != 0; - - //if marked as spent, check that it actually made it into the chain - CTransaction txSpend; - uint256 hashBlockSpend; - if (fSpent && !GetTransaction(hashTxSpend, txSpend, hashBlockSpend, true)) { - LogPrintf("%s : cannot find spend tx %s\n", __func__, hashTxSpend.GetHex()); - zerocoinDB->EraseCoinSpend(mint.GetSerialNumber()); - mint.SetUsed(false); - vMintsToUpdate.push_back(mint); - continue; - } - - //The mint has been incorrectly labelled as spent in zerocoinDB and needs to be undone - int nHeightTx = 0; - if (fSpent && !IsSerialInBlockchain(mint.GetSerialNumber(), nHeightTx)) { - LogPrintf("%s : cannot find block %s. Erasing coinspend from zerocoinDB.\n", __func__, hashBlockSpend.GetHex()); - zerocoinDB->EraseCoinSpend(mint.GetSerialNumber()); - mint.SetUsed(false); - vMintsToUpdate.push_back(mint); - continue; - } - - // if meta data is correct, then no need to update - if (mint.GetTxHash() == txHash && mint.GetHeight() == mapBlockIndex[hashBlock]->nHeight && mint.IsUsed() == fSpent) - continue; - - //mark this mint for update - mint.SetTxHash(txHash); - mint.SetHeight(mapBlockIndex[hashBlock]->nHeight); - mint.SetUsed(fSpent); - - vMintsToUpdate.push_back(mint); - } - - if (fExtendedSearch) { - // search the blockchain for the meta data on our missing mints - int nZerocoinStartHeight = GetZerocoinStartHeight(); - - for (int i = nZerocoinStartHeight; i < chainActive.Height(); i++) { - if (i % 1000 == 0) - LogPrintf("%s : scanned %d blocks\n", __func__, i - nZerocoinStartHeight); - - if (chainActive[i]->vMintDenominationsInBlock.empty()) - continue; - - CBlock block; - if (!ReadBlockFromDisk(block, chainActive[i])) - continue; - - list vMints; - if (!BlockToZerocoinMintList(block, vMints)) - continue; - - // search the blocks mints to see if it contains the mint that is requesting meta data updates - for (CZerocoinMint mintBlockChain : vMints) { - for (CZerocoinMint mintMissing : vMissingMints) { - if (mintMissing.GetValue() == mintBlockChain.GetValue()) { - LogPrintf("%s FOUND %s in block %d\n", __func__, mintMissing.GetValue().GetHex(), i); - mintMissing.SetHeight(i); - mintMissing.SetTxHash(mintBlockChain.GetTxHash()); - vMintsToUpdate.push_back(mintMissing); - } - } - } - } - } - - //remove any missing mints that were found - for (CZerocoinMint mintMissing : vMissingMints) { - for (CZerocoinMint mintFound : vMintsToUpdate) { - if (mintMissing.GetValue() == mintFound.GetValue()) - std::remove(vMissingMints.begin(), vMissingMints.end(), mintMissing); - } - } + // see which mints are in our public zerocoin database. The mint should be here if it exists, unless + // something went wrong + for (CZerocoinMint mint : vMintsToFind) { + uint256 txHash; + if (!zerocoinDB->ReadCoinMint(mint.GetValue(), txHash)) { + vMissingMints.push_back(mint); + continue; + } + + // make sure the txhash and block height meta data are correct for this mint + CTransaction tx; + uint256 hashBlock; + if (!GetTransaction(txHash, tx, hashBlock, true)) { + LogPrintf("%s : cannot find tx %s\n", __func__, txHash.GetHex()); + vMissingMints.push_back(mint); + continue; + } + + if (!mapBlockIndex.count(hashBlock)) { + LogPrintf("%s : cannot find block %s\n", __func__, hashBlock.GetHex()); + vMissingMints.push_back(mint); + continue; + } + + //see if this mint is spent + uint256 hashTxSpend = 0; + zerocoinDB->ReadCoinSpend(mint.GetSerialNumber(), hashTxSpend); + bool fSpent = hashTxSpend != 0; + + //if marked as spent, check that it actually made it into the chain + CTransaction txSpend; + uint256 hashBlockSpend; + if (fSpent && !GetTransaction(hashTxSpend, txSpend, hashBlockSpend, true)) { + LogPrintf("%s : cannot find spend tx %s\n", __func__, hashTxSpend.GetHex()); + zerocoinDB->EraseCoinSpend(mint.GetSerialNumber()); + mint.SetUsed(false); + vMintsToUpdate.push_back(mint); + continue; + } + + //The mint has been incorrectly labelled as spent in zerocoinDB and needs to be undone + int nHeightTx = 0; + if (fSpent && !IsSerialInBlockchain(mint.GetSerialNumber(), nHeightTx)) { + LogPrintf("%s : cannot find block %s. Erasing coinspend from zerocoinDB.\n", __func__, hashBlockSpend.GetHex()); + zerocoinDB->EraseCoinSpend(mint.GetSerialNumber()); + mint.SetUsed(false); + vMintsToUpdate.push_back(mint); + continue; + } + + // if meta data is correct, then no need to update + if (mint.GetTxHash() == txHash && mint.GetHeight() == mapBlockIndex[hashBlock]->nHeight && mint.IsUsed() == fSpent) + continue; + + //mark this mint for update + mint.SetTxHash(txHash); + mint.SetHeight(mapBlockIndex[hashBlock]->nHeight); + mint.SetUsed(fSpent); + + vMintsToUpdate.push_back(mint); + } + + if (fExtendedSearch) { + // search the blockchain for the meta data on our missing mints + int nZerocoinStartHeight = GetZerocoinStartHeight(); + + for (int i = nZerocoinStartHeight; i < chainActive.Height(); i++) { + if (i % 1000 == 0) + LogPrintf("%s : scanned %d blocks\n", __func__, i - nZerocoinStartHeight); + + if (chainActive[i]->vMintDenominationsInBlock.empty()) + continue; + + CBlock block; + if (!ReadBlockFromDisk(block, chainActive[i])) + continue; + + list vMints; + if (!BlockToZerocoinMintList(block, vMints)) + continue; + + // search the blocks mints to see if it contains the mint that is requesting meta data updates + for (CZerocoinMint mintBlockChain : vMints) { + for (CZerocoinMint mintMissing : vMissingMints) { + if (mintMissing.GetValue() == mintBlockChain.GetValue()) { + LogPrintf("%s FOUND %s in block %d\n", __func__, mintMissing.GetValue().GetHex(), i); + mintMissing.SetHeight(i); + mintMissing.SetTxHash(mintBlockChain.GetTxHash()); + vMintsToUpdate.push_back(mintMissing); + } + } + } + } + } + + //remove any missing mints that were found + for (CZerocoinMint mintMissing : vMissingMints) { + for (CZerocoinMint mintFound : vMintsToUpdate) { + if (mintMissing.GetValue() == mintFound.GetValue()) + std::remove(vMissingMints.begin(), vMissingMints.end(), mintMissing); + } + } } bool GetZerocoinMint(const CBigNum& bnPubcoin, uint256& txHash) { - txHash = 0; - return zerocoinDB->ReadCoinMint(bnPubcoin, txHash); + txHash = 0; + return zerocoinDB->ReadCoinMint(bnPubcoin, txHash); } bool IsSerialKnown(const CBigNum& bnSerial) { - uint256 txHash = 0; - return zerocoinDB->ReadCoinSpend(bnSerial, txHash); + uint256 txHash = 0; + return zerocoinDB->ReadCoinSpend(bnSerial, txHash); } bool IsSerialInBlockchain(const CBigNum& bnSerial, int& nHeightTx) { - uint256 txHash = 0; - // if not in zerocoinDB then its not in the blockchain - if (!zerocoinDB->ReadCoinSpend(bnSerial, txHash)) - return false; + uint256 txHash = 0; + // if not in zerocoinDB then its not in the blockchain + if (!zerocoinDB->ReadCoinSpend(bnSerial, txHash)) + return false; - CTransaction tx; - uint256 hashBlock; - if (!GetTransaction(txHash, tx, hashBlock, true)) - return false; + CTransaction tx; + uint256 hashBlock; + if (!GetTransaction(txHash, tx, hashBlock, true)) + return false; - bool inChain = mapBlockIndex.count(hashBlock) && chainActive.Contains(mapBlockIndex[hashBlock]); - if (inChain) - nHeightTx = mapBlockIndex.at(hashBlock)->nHeight; + bool inChain = mapBlockIndex.count(hashBlock) && chainActive.Contains(mapBlockIndex[hashBlock]); + if (inChain) + nHeightTx = mapBlockIndex.at(hashBlock)->nHeight; - return inChain; + return inChain; } bool RemoveSerialFromDB(const CBigNum& bnSerial) { - return zerocoinDB->EraseCoinSpend(bnSerial); + return zerocoinDB->EraseCoinSpend(bnSerial); } /** zerocoin transaction checks */ bool RecordMintToDB(PublicCoin publicZerocoin, const uint256& txHash) { - //Check the pubCoinValue didn't already store in the zerocoin database. todo: pubcoin memory map? - //write the zerocoinmint to db if we don't already have it - //note that many of the mint parameters are not set here because those params are private to the minter - CZerocoinMint pubCoinTx; - uint256 hashFromDB; - if (zerocoinDB->ReadCoinMint(publicZerocoin.getValue(), hashFromDB)) { - if (hashFromDB == txHash) - return true; + //Check the pubCoinValue didn't already store in the zerocoin database. todo: pubcoin memory map? + //write the zerocoinmint to db if we don't already have it + //note that many of the mint parameters are not set here because those params are private to the minter + CZerocoinMint pubCoinTx; + uint256 hashFromDB; + if (zerocoinDB->ReadCoinMint(publicZerocoin.getValue(), hashFromDB)) { + if (hashFromDB == txHash) + return true; - LogPrintf("RecordMintToDB: failed, we already have this public coin recorded\n"); - return false; - } + LogPrintf("RecordMintToDB: failed, we already have this public coin recorded\n"); + return false; + } - if (!zerocoinDB->WriteCoinMint(publicZerocoin, txHash)) { - LogPrintf("RecordMintToDB: failed to record public coin to DB\n"); - return false; - } + if (!zerocoinDB->WriteCoinMint(publicZerocoin, txHash)) { + LogPrintf("RecordMintToDB: failed to record public coin to DB\n"); + return false; + } - return true; + return true; } bool TxOutToPublicCoin(const CTxOut txout, PublicCoin& pubCoin, CValidationState& state) { - CBigNum publicZerocoin; - vector vchZeroMint; - vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + SCRIPT_OFFSET, - txout.scriptPubKey.begin() + txout.scriptPubKey.size()); - publicZerocoin.setvch(vchZeroMint); + CBigNum publicZerocoin; + vector vchZeroMint; + vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + SCRIPT_OFFSET, + txout.scriptPubKey.begin() + txout.scriptPubKey.size()); + publicZerocoin.setvch(vchZeroMint); - CoinDenomination denomination = AmountToZerocoinDenomination(txout.nValue); - LogPrint("zero", "%s ZCPRINT denomination %d pubcoin %s\n", __func__, denomination, publicZerocoin.GetHex()); - if (denomination == ZQ_ERROR) - return state.DoS(100, error("TxOutToPublicCoin : txout.nValue is not correct")); + CoinDenomination denomination = AmountToZerocoinDenomination(txout.nValue); + LogPrint("zero", "%s ZCPRINT denomination %d pubcoin %s\n", __func__, denomination, publicZerocoin.GetHex()); + if (denomination == ZQ_ERROR) + return state.DoS(100, error("TxOutToPublicCoin : txout.nValue is not correct")); - PublicCoin checkPubCoin(Params().Zerocoin_Params(), publicZerocoin, denomination); - pubCoin = checkPubCoin; + PublicCoin checkPubCoin(Params().Zerocoin_Params(), publicZerocoin, denomination); + pubCoin = checkPubCoin; - return true; + return true; } bool BlockToPubcoinList(const CBlock& block, list& listPubcoins) { - for (const CTransaction tx : block.vtx) { - if (!tx.IsZerocoinMint()) - continue; + for (const CTransaction tx : block.vtx) { + if (!tx.IsZerocoinMint()) + continue; - for (unsigned int i = 0; i < tx.vout.size(); i++) { - const CTxOut txOut = tx.vout[i]; - if (!txOut.scriptPubKey.IsZerocoinMint()) - continue; + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut txOut = tx.vout[i]; + if (!txOut.scriptPubKey.IsZerocoinMint()) + continue; - CValidationState state; - PublicCoin pubCoin(Params().Zerocoin_Params()); - if (!TxOutToPublicCoin(txOut, pubCoin, state)) - return false; + CValidationState state; + PublicCoin pubCoin(Params().Zerocoin_Params()); + if (!TxOutToPublicCoin(txOut, pubCoin, state)) + return false; - listPubcoins.emplace_back(pubCoin); - } - } + listPubcoins.emplace_back(pubCoin); + } + } - return true; + return true; } //return a list of zerocoin mints contained in a specific block bool BlockToZerocoinMintList(const CBlock& block, std::list& vMints) { - for (const CTransaction tx : block.vtx) { - if (!tx.IsZerocoinMint()) - continue; + for (const CTransaction tx : block.vtx) { + if (!tx.IsZerocoinMint()) + continue; - for (unsigned int i = 0; i < tx.vout.size(); i++) { - const CTxOut txOut = tx.vout[i]; - if (!txOut.scriptPubKey.IsZerocoinMint()) - continue; + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut txOut = tx.vout[i]; + if (!txOut.scriptPubKey.IsZerocoinMint()) + continue; - CValidationState state; - PublicCoin pubCoin(Params().Zerocoin_Params()); - if (!TxOutToPublicCoin(txOut, pubCoin, state)) - return false; + CValidationState state; + PublicCoin pubCoin(Params().Zerocoin_Params()); + if (!TxOutToPublicCoin(txOut, pubCoin, state)) + return false; - CZerocoinMint mint = CZerocoinMint(pubCoin.getDenomination(), pubCoin.getValue(), 0, 0, false); - mint.SetTxHash(tx.GetHash()); - vMints.push_back(mint); - } - } + CZerocoinMint mint = CZerocoinMint(pubCoin.getDenomination(), pubCoin.getValue(), 0, 0, false); + mint.SetTxHash(tx.GetHash()); + vMints.push_back(mint); + } + } - return true; + return true; } bool BlockToMintValueVector(const CBlock& block, const CoinDenomination denom, vector& vValues) { - for (const CTransaction tx : block.vtx) { - if (!tx.IsZerocoinMint()) - continue; + for (const CTransaction tx : block.vtx) { + if (!tx.IsZerocoinMint()) + continue; - for (const CTxOut txOut : tx.vout) { - if (!txOut.scriptPubKey.IsZerocoinMint()) - continue; + for (const CTxOut txOut : tx.vout) { + if (!txOut.scriptPubKey.IsZerocoinMint()) + continue; - CValidationState state; - PublicCoin coin(Params().Zerocoin_Params()); - if (!TxOutToPublicCoin(txOut, coin, state)) - return false; + CValidationState state; + PublicCoin coin(Params().Zerocoin_Params()); + if (!TxOutToPublicCoin(txOut, coin, state)) + return false; - if (coin.getDenomination() != denom) - continue; + if (coin.getDenomination() != denom) + continue; - vValues.push_back(coin.getValue()); - } - } + vValues.push_back(coin.getValue()); + } + } - return true; + return true; } //return a list of zerocoin spends contained in a specific block, list may have many denominations std::list ZerocoinSpendListFromBlock(const CBlock& block) { - std::list vSpends; - for (const CTransaction tx : block.vtx) { - if (!tx.IsZerocoinSpend()) - continue; + std::list vSpends; + for (const CTransaction tx : block.vtx) { + if (!tx.IsZerocoinSpend()) + continue; - for (const CTxIn txin : tx.vin) { - if (!txin.scriptSig.IsZerocoinSpend()) - continue; + for (const CTxIn txin : tx.vin) { + if (!txin.scriptSig.IsZerocoinSpend()) + continue; - libzerocoin::CoinDenomination c = libzerocoin::IntToZerocoinDenomination(txin.nSequence); - vSpends.push_back(c); - } - } - return vSpends; + libzerocoin::CoinDenomination c = libzerocoin::IntToZerocoinDenomination(txin.nSequence); + vSpends.push_back(c); + } + } + return vSpends; } bool CheckZerocoinMint(const uint256& txHash, const CTxOut& txout, CValidationState& state, bool fCheckOnly) { - PublicCoin pubCoin(Params().Zerocoin_Params()); - if (!TxOutToPublicCoin(txout, pubCoin, state)) - return state.DoS(100, error("CheckZerocoinMint(): TxOutToPublicCoin() failed")); + PublicCoin pubCoin(Params().Zerocoin_Params()); + if (!TxOutToPublicCoin(txout, pubCoin, state)) + return state.DoS(100, error("CheckZerocoinMint(): TxOutToPublicCoin() failed")); - if (!pubCoin.validate()) - return state.DoS(100, error("CheckZerocoinMint() : PubCoin does not validate\n")); + if (!pubCoin.validate()) + return state.DoS(100, error("CheckZerocoinMint() : PubCoin does not validate\n")); - if (!fCheckOnly && !RecordMintToDB(pubCoin, txHash)) - return state.DoS(100, error("CheckZerocoinMint(): RecordMintToDB() failed")); + if (!fCheckOnly && !RecordMintToDB(pubCoin, txHash)) + return state.DoS(100, error("CheckZerocoinMint(): RecordMintToDB() failed")); - return true; + return true; } CoinSpend TxInToZerocoinSpend(const CTxIn& txin) { - // Deserialize the CoinSpend intro a fresh object - std::vector > dataTxIn; - dataTxIn.insert(dataTxIn.end(), txin.scriptSig.begin() + BIGNUM_SIZE, txin.scriptSig.end()); + // Deserialize the CoinSpend intro a fresh object + std::vector > dataTxIn; + dataTxIn.insert(dataTxIn.end(), txin.scriptSig.begin() + BIGNUM_SIZE, txin.scriptSig.end()); - CDataStream serializedCoinSpend(dataTxIn, SER_NETWORK, PROTOCOL_VERSION); - return CoinSpend(Params().Zerocoin_Params(), serializedCoinSpend); + CDataStream serializedCoinSpend(dataTxIn, SER_NETWORK, PROTOCOL_VERSION); + return CoinSpend(Params().Zerocoin_Params(), serializedCoinSpend); } bool IsZerocoinSpendUnknown(CoinSpend coinSpend, uint256 hashTx, CValidationState& state) { - uint256 hashTxFromDB; - if (zerocoinDB->ReadCoinSpend(coinSpend.getCoinSerialNumber(), hashTxFromDB)) - return hashTx == hashTxFromDB; + uint256 hashTxFromDB; + if (zerocoinDB->ReadCoinSpend(coinSpend.getCoinSerialNumber(), hashTxFromDB)) + return hashTx == hashTxFromDB; - if (!zerocoinDB->WriteCoinSpend(coinSpend.getCoinSerialNumber(), hashTx)) - return state.DoS(100, error("CheckZerocoinSpend(): Failed to write zerocoin mint to database")); + if (!zerocoinDB->WriteCoinSpend(coinSpend.getCoinSerialNumber(), hashTx)) + return state.DoS(100, error("CheckZerocoinSpend(): Failed to write zerocoin mint to database")); - return true; + return true; } bool CheckZerocoinSpend(const CTransaction tx, bool fVerifySignature, CValidationState& state) { - //max needed non-mint outputs should be 2 - one for redemption address and a possible 2nd for change - if (tx.vout.size() > 2) { - int outs = 0; - for (const CTxOut out : tx.vout) { - if (out.IsZerocoinMint()) - continue; - outs++; - } - if (outs > 2) - return state.DoS(100, error("CheckZerocoinSpend(): over two non-mint outputs in a zerocoinspend transaction")); - } - - //compute the txout hash that is used for the zerocoinspend signatures - CMutableTransaction txTemp; - for (const CTxOut out : tx.vout) { - txTemp.vout.push_back(out); - } - uint256 hashTxOut = txTemp.GetHash(); - - bool fValidated = false; - set serials; - list vSpends; - CAmount nTotalRedeemed = 0; - for (const CTxIn& txin : tx.vin) { - //only check txin that is a zcspend - if (!txin.scriptSig.IsZerocoinSpend()) - continue; - - CoinSpend newSpend = TxInToZerocoinSpend(txin); - vSpends.push_back(newSpend); - - //check that the denomination is valid - if (newSpend.getDenomination() == ZQ_ERROR) - return state.DoS(100, error("Zerocoinspend does not have the correct denomination")); - - //check that denomination is what it claims to be in nSequence - if (newSpend.getDenomination() != txin.nSequence) - return state.DoS(100, error("Zerocoinspend nSequence denomination does not match CoinSpend")); - - //make sure the txout has not changed - if (newSpend.getTxOutHash() != hashTxOut) - return state.DoS(100, error("Zerocoinspend does not use the same txout that was used in the SoK")); - - // Skip signature verification during initial block download - if (fVerifySignature) { - //see if we have record of the accumulator used in the spend tx - CBigNum bnAccumulatorValue = 0; - if (!zerocoinDB->ReadAccumulatorValue(newSpend.getAccumulatorChecksum(), bnAccumulatorValue)) - return state.DoS(100, error("Zerocoinspend could not find accumulator associated with checksum")); - - Accumulator accumulator(Params().Zerocoin_Params(), newSpend.getDenomination(), bnAccumulatorValue); - - //Check that the coin is on the accumulator - if (!newSpend.Verify(accumulator)) - return state.DoS(100, error("CheckZerocoinSpend(): zerocoin spend did not verify")); - } - - if (serials.count(newSpend.getCoinSerialNumber())) - return state.DoS(100, error("Zerocoinspend serial is used twice in the same tx")); - serials.insert(newSpend.getCoinSerialNumber()); - - //make sure that there is no over redemption of coins - nTotalRedeemed += ZerocoinDenominationToAmount(newSpend.getDenomination()); - fValidated = true; - } - - if (nTotalRedeemed < tx.GetValueOut()) { - LogPrintf("redeemed = %s , spend = %s \n", FormatMoney(nTotalRedeemed), FormatMoney(tx.GetValueOut())); - return state.DoS(100, error("Transaction spend more than was redeemed in zerocoins")); - } - - // Send signal to wallet if this is ours - if (pwalletMain) { - CWalletDB walletdb(pwalletMain->strWalletFile); - list listMySerials = walletdb.ListMintedCoinsSerial(); - for (const auto& newSpend : vSpends) { - list::iterator it = find(listMySerials.begin(), listMySerials.end(), newSpend.getCoinSerialNumber()); - if (it != listMySerials.end()) { - LogPrintf("%s: %s detected spent zerocoin mint in transaction %s \n", __func__, it->GetHex(), tx.GetHash().GetHex()); - pwalletMain->NotifyZerocoinChanged(pwalletMain, it->GetHex(), "Used", CT_UPDATED); - } - } - } - - return fValidated; + //max needed non-mint outputs should be 2 - one for redemption address and a possible 2nd for change + if (tx.vout.size() > 2) { + int outs = 0; + for (const CTxOut out : tx.vout) { + if (out.IsZerocoinMint()) + continue; + outs++; + } + if (outs > 2) + return state.DoS(100, error("CheckZerocoinSpend(): over two non-mint outputs in a zerocoinspend transaction")); + } + + //compute the txout hash that is used for the zerocoinspend signatures + CMutableTransaction txTemp; + for (const CTxOut out : tx.vout) { + txTemp.vout.push_back(out); + } + uint256 hashTxOut = txTemp.GetHash(); + + bool fValidated = false; + set serials; + list vSpends; + CAmount nTotalRedeemed = 0; + for (const CTxIn& txin : tx.vin) { + //only check txin that is a zcspend + if (!txin.scriptSig.IsZerocoinSpend()) + continue; + + CoinSpend newSpend = TxInToZerocoinSpend(txin); + vSpends.push_back(newSpend); + + //check that the denomination is valid + if (newSpend.getDenomination() == ZQ_ERROR) + return state.DoS(100, error("Zerocoinspend does not have the correct denomination")); + + //check that denomination is what it claims to be in nSequence + if (newSpend.getDenomination() != txin.nSequence) + return state.DoS(100, error("Zerocoinspend nSequence denomination does not match CoinSpend")); + + //make sure the txout has not changed + if (newSpend.getTxOutHash() != hashTxOut) + return state.DoS(100, error("Zerocoinspend does not use the same txout that was used in the SoK")); + + // Skip signature verification during initial block download + if (fVerifySignature) { + //see if we have record of the accumulator used in the spend tx + CBigNum bnAccumulatorValue = 0; + if (!zerocoinDB->ReadAccumulatorValue(newSpend.getAccumulatorChecksum(), bnAccumulatorValue)) + return state.DoS(100, error("Zerocoinspend could not find accumulator associated with checksum")); + + Accumulator accumulator(Params().Zerocoin_Params(), newSpend.getDenomination(), bnAccumulatorValue); + + //Check that the coin is on the accumulator + if (!newSpend.Verify(accumulator)) + return state.DoS(100, error("CheckZerocoinSpend(): zerocoin spend did not verify")); + } + + if (serials.count(newSpend.getCoinSerialNumber())) + return state.DoS(100, error("Zerocoinspend serial is used twice in the same tx")); + serials.insert(newSpend.getCoinSerialNumber()); + + //make sure that there is no over redemption of coins + nTotalRedeemed += ZerocoinDenominationToAmount(newSpend.getDenomination()); + fValidated = true; + } + + if (nTotalRedeemed < tx.GetValueOut()) { + LogPrintf("redeemed = %s , spend = %s \n", FormatMoney(nTotalRedeemed), FormatMoney(tx.GetValueOut())); + return state.DoS(100, error("Transaction spend more than was redeemed in zerocoins")); + } + + // Send signal to wallet if this is ours + if (pwalletMain) { + CWalletDB walletdb(pwalletMain->strWalletFile); + list listMySerials = walletdb.ListMintedCoinsSerial(); + for (const auto& newSpend : vSpends) { + list::iterator it = find(listMySerials.begin(), listMySerials.end(), newSpend.getCoinSerialNumber()); + if (it != listMySerials.end()) { + LogPrintf("%s: %s detected spent zerocoin mint in transaction %s \n", __func__, it->GetHex(), tx.GetHash().GetHex()); + pwalletMain->NotifyZerocoinChanged(pwalletMain, it->GetHex(), "Used", CT_UPDATED); + } + } + } + + return fValidated; } bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, bool fRejectBadUTXO, CValidationState& state) { - // Basic checks that don't depend on any context - if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction() : vin empty"), - REJECT_INVALID, "bad-txns-vin-empty"); - if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction() : vout empty"), - REJECT_INVALID, "bad-txns-vout-empty"); - - // Size limits - unsigned int nMaxSize = MAX_ZEROCOIN_TX_SIZE; - - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > nMaxSize) - return state.DoS(100, error("CheckTransaction() : size limits failed"), - REJECT_INVALID, "bad-txns-oversize"); - - // Check for negative or overflow output values - CAmount nValueOut = 0; - int nZCSpendCount = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake()) - return state.DoS(100, error("CheckTransaction(): txout empty for user transaction")); - - if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), - REJECT_INVALID, "bad-txns-vout-negative"); - if (txout.nValue > Params().MaxMoneyOut()) - return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), - REJECT_INVALID, "bad-txns-vout-toolarge"); - nValueOut += txout.nValue; - if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CheckTransaction() : txout total out of range"), - REJECT_INVALID, "bad-txns-txouttotal-toolarge"); - if (fZerocoinActive && txout.IsZerocoinMint()) { - if (!CheckZerocoinMint(tx.GetHash(), txout, state, false)) { - if (fRejectBadUTXO) - return state.DoS(100, error("CheckTransaction() : invalid zerocoin mint")); - } - } - if (fZerocoinActive && txout.scriptPubKey.IsZerocoinSpend()) - nZCSpendCount++; - } - - if (fZerocoinActive) { - if (nZCSpendCount > Params().Zerocoin_MaxSpendsPerTransaction()) - return state.DoS(100, error("CheckTransaction() : there are more zerocoin spends than are allowed in one transaction")); - - if (tx.IsZerocoinSpend()) { - //require that a zerocoinspend only has inputs that are zerocoins - for (const CTxIn in : tx.vin) { - if (!in.scriptSig.IsZerocoinSpend()) - return state.DoS(100, - error("CheckTransaction() : zerocoinspend contains inputs that are not zerocoins")); - } - - // Do not require signature verification if this is initial sync and a block over 24 hours old - bool fVerifySignature = !IsInitialBlockDownload() && (GetTime() - chainActive.Tip()->GetBlockTime() < (60 * 60 * 24)); - if (!CheckZerocoinSpend(tx, fVerifySignature, state)) - return state.DoS(100, error("CheckTransaction() : invalid zerocoin spend")); - } - } - - // Check for duplicate inputs - set vInOutPoints; - set vZerocoinSpendSerials; - for (const CTxIn& txin : tx.vin) { - if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CheckTransaction() : duplicate inputs"), - REJECT_INVALID, "bad-txns-inputs-duplicate"); - - //duplicate zcspend serials are checked in CheckZerocoinSpend() - if (!txin.scriptSig.IsZerocoinSpend()) - vInOutPoints.insert(txin.prevout); - } - - if (tx.IsCoinBase()) { - if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 150) - return state.DoS(100, error("CheckTransaction() : coinbase script size=%d", tx.vin[0].scriptSig.size()), - REJECT_INVALID, "bad-cb-length"); - } - else if (fZerocoinActive && tx.IsZerocoinSpend()) { - if (tx.vin.size() < 1 || static_cast(tx.vin.size()) > Params().Zerocoin_MaxSpendsPerTransaction()) - return state.DoS(10, error("CheckTransaction() : Zerocoin Spend has more than allowed txin's"), REJECT_INVALID, "bad-zerocoinspend"); - } - else { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (txin.prevout.IsNull() && (fZerocoinActive && !txin.scriptSig.IsZerocoinSpend())) - return state.DoS(10, error("CheckTransaction() : prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); - } + // Basic checks that don't depend on any context + if (tx.vin.empty()) + return state.DoS(10, error("CheckTransaction() : vin empty"), + REJECT_INVALID, "bad-txns-vin-empty"); + if (tx.vout.empty()) + return state.DoS(10, error("CheckTransaction() : vout empty"), + REJECT_INVALID, "bad-txns-vout-empty"); + + // Size limits + unsigned int nMaxSize = MAX_ZEROCOIN_TX_SIZE; + + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > nMaxSize) + return state.DoS(100, error("CheckTransaction() : size limits failed"), + REJECT_INVALID, "bad-txns-oversize"); + + // Check for negative or overflow output values + CAmount nValueOut = 0; + int nZCSpendCount = 0; + BOOST_FOREACH (const CTxOut& txout, tx.vout) { + if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake()) + return state.DoS(100, error("CheckTransaction(): txout empty for user transaction")); + + if (txout.nValue < 0) + return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), + REJECT_INVALID, "bad-txns-vout-negative"); + if (txout.nValue > Params().MaxMoneyOut()) + return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), + REJECT_INVALID, "bad-txns-vout-toolarge"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return state.DoS(100, error("CheckTransaction() : txout total out of range"), + REJECT_INVALID, "bad-txns-txouttotal-toolarge"); + if (fZerocoinActive && txout.IsZerocoinMint()) { + if (!CheckZerocoinMint(tx.GetHash(), txout, state, false)) { + if (fRejectBadUTXO) + return state.DoS(100, error("CheckTransaction() : invalid zerocoin mint")); + } + } + if (fZerocoinActive && txout.scriptPubKey.IsZerocoinSpend()) + nZCSpendCount++; + } + + if (fZerocoinActive) { + if (nZCSpendCount > Params().Zerocoin_MaxSpendsPerTransaction()) + return state.DoS(100, error("CheckTransaction() : there are more zerocoin spends than are allowed in one transaction")); + + if (tx.IsZerocoinSpend()) { + //require that a zerocoinspend only has inputs that are zerocoins + for (const CTxIn in : tx.vin) { + if (!in.scriptSig.IsZerocoinSpend()) + return state.DoS(100, + error("CheckTransaction() : zerocoinspend contains inputs that are not zerocoins")); + } - return true; + // Do not require signature verification if this is initial sync and a block over 24 hours old + bool fVerifySignature = !IsInitialBlockDownload() && (GetTime() - chainActive.Tip()->GetBlockTime() < (60 * 60 * 24)); + if (!CheckZerocoinSpend(tx, fVerifySignature, state)) + return state.DoS(100, error("CheckTransaction() : invalid zerocoin spend")); + } + } + + // Check for duplicate inputs + set vInOutPoints; + set vZerocoinSpendSerials; + for (const CTxIn& txin : tx.vin) { + if (vInOutPoints.count(txin.prevout)) + return state.DoS(100, error("CheckTransaction() : duplicate inputs"), + REJECT_INVALID, "bad-txns-inputs-duplicate"); + + //duplicate zcspend serials are checked in CheckZerocoinSpend() + if (!txin.scriptSig.IsZerocoinSpend()) + vInOutPoints.insert(txin.prevout); + } + + if (tx.IsCoinBase()) { + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 150) + return state.DoS(100, error("CheckTransaction() : coinbase script size=%d", tx.vin[0].scriptSig.size()), + REJECT_INVALID, "bad-cb-length"); + } else if (fZerocoinActive && tx.IsZerocoinSpend()) { + if (tx.vin.size() < 1 || static_cast(tx.vin.size()) > Params().Zerocoin_MaxSpendsPerTransaction()) + return state.DoS(10, error("CheckTransaction() : Zerocoin Spend has more than allowed txin's"), REJECT_INVALID, "bad-zerocoinspend"); + } else { + BOOST_FOREACH (const CTxIn& txin, tx.vin) + if (txin.prevout.IsNull() && (fZerocoinActive && !txin.scriptSig.IsZerocoinSpend())) + return state.DoS(10, error("CheckTransaction() : prevout is null"), + REJECT_INVALID, "bad-txns-prevout-null"); + } + + return true; } bool CheckFinalTx(const CTransaction& tx, int flags) { - AssertLockHeld(cs_main); + AssertLockHeld(cs_main); - // By convention a negative value for flags indicates that the - // current network-enforced consensus rules should be used. In - // a future soft-fork scenario that would mean checking which - // rules would be enforced for the next block and setting the - // appropriate flags. At the present time no soft-forks are - // scheduled, so no flags are set. - flags = std::max(flags, 0); + // By convention a negative value for flags indicates that the + // current network-enforced consensus rules should be used. In + // a future soft-fork scenario that would mean checking which + // rules would be enforced for the next block and setting the + // appropriate flags. At the present time no soft-forks are + // scheduled, so no flags are set. + flags = std::max(flags, 0); - // CheckFinalTx() uses chainActive.Height()+1 to evaluate - // nLockTime because when IsFinalTx() is called within - // CBlock::AcceptBlock(), the height of the block *being* - // evaluated is what is used. Thus if we want to know if a - // transaction can be part of the *next* block, we need to call - // IsFinalTx() with one more than chainActive.Height(). - const int nBlockHeight = chainActive.Height() + 1; + // CheckFinalTx() uses chainActive.Height()+1 to evaluate + // nLockTime because when IsFinalTx() is called within + // CBlock::AcceptBlock(), the height of the block *being* + // evaluated is what is used. Thus if we want to know if a + // transaction can be part of the *next* block, we need to call + // IsFinalTx() with one more than chainActive.Height(). + const int nBlockHeight = chainActive.Height() + 1; - // BIP113 will require that time-locked transactions have nLockTime set to - // less than the median time of the previous block they're contained in. - // When the next block is created its previous block will be the current - // chain tip, so we use that to calculate the median time passed to - // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. - const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() : GetAdjustedTime(); + // BIP113 will require that time-locked transactions have nLockTime set to + // less than the median time of the previous block they're contained in. + // When the next block is created its previous block will be the current + // chain tip, so we use that to calculate the median time passed to + // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. + const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() : GetAdjustedTime(); - return IsFinalTx(tx, nBlockHeight, nBlockTime); + return IsFinalTx(tx, nBlockHeight, nBlockTime); } CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) { - { - LOCK(mempool.cs); - uint256 hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (dPriorityDelta > 0 || nFeeDelta > 0) - return 0; - } + { + LOCK(mempool.cs); + uint256 hash = tx.GetHash(); + double dPriorityDelta = 0; + CAmount nFeeDelta = 0; + mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); + if (dPriorityDelta > 0 || nFeeDelta > 0) + return 0; + } - CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); + CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - if (fAllowFree) { - // There is a free transaction area in blocks created by most miners, - // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 - // to be considered to fall into this category. We don't want to encourage sending - // multiple transactions instead of one big transaction to avoid fees. - if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) - nMinFee = 0; - } + if (fAllowFree) { + // There is a free transaction area in blocks created by most miners, + // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 + // to be considered to fall into this category. We don't want to encourage sending + // multiple transactions instead of one big transaction to avoid fees. + if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) + nMinFee = 0; + } - if (!MoneyRange(nMinFee)) - nMinFee = Params().MaxMoneyOut(); - return nMinFee; + if (!MoneyRange(nMinFee)) + nMinFee = Params().MaxMoneyOut(); + return nMinFee; } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee, bool ignoreFees) { - AssertLockHeld(cs_main); - if (pfMissingInputs) - *pfMissingInputs = false; - - //Temporarily disable zerocoin for maintenance - if (GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && tx.ContainsZerocoins()) - return state.DoS(10, error("AcceptToMemoryPool : Zerocoin transactions are temporarily disabled for maintenance"), REJECT_INVALID, "bad-tx"); - - if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_AccumulatorStartHeight(), true, state)) - return state.DoS(100, error("AcceptToMemoryPool: : CheckTransaction failed"), REJECT_INVALID, "bad-tx"); - - // Coinbase is only valid in a block, not as a loose transaction - if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"), - REJECT_INVALID, "coinbase"); - - //Coinstake is also only valid in a block, not as a loose transaction - if (tx.IsCoinStake()) - return state.DoS(100, error("AcceptToMemoryPool: coinstake as individual tx"), - REJECT_INVALID, "coinstake"); - - // Rather not work on nonstandard transactions (unless -testnet/-regtest) - string reason; - if (Params().RequireStandard() && !IsStandardTx(tx, reason)) - return state.DoS(0, - error("AcceptToMemoryPool : nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); - // is it already in the memory pool? - uint256 hash = tx.GetHash(); - if (pool.exists(hash)) { - LogPrintf("%s tx already in mempool\n", __func__); - return false; - } - - // ----------- swiftTX transaction scanning ----------- - - BOOST_FOREACH(const CTxIn& in, tx.vin) { - if (mapLockedInputs.count(in.prevout)) { - if (mapLockedInputs[in.prevout] != tx.GetHash()) { - return state.DoS(0, - error("AcceptToMemoryPool : conflicts with existing transaction lock: %s", reason), - REJECT_INVALID, "tx-lock-conflict"); - } - } - } - - // Check for conflicts with in-memory transactions - if (!tx.IsZerocoinSpend()) { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) { - // Disable replacement feature for now - return false; - } - } - } - - - { - CCoinsView dummy; - CCoinsViewCache view(&dummy); - - CAmount nValueIn = 0; - if (tx.IsZerocoinSpend()) { - nValueIn = tx.GetZerocoinSpent(); - - //Check that txid is not already in the chain - int nHeightTx = 0; - if (IsTransactionInChain(tx.GetHash(), nHeightTx)) - return state.Invalid(error("AcceptToMemoryPool : zCcbc spend tx %s already in block %d", tx.GetHash().GetHex(), nHeightTx), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); - - //Check for double spending of serial #'s - for (const CTxIn& txIn : tx.vin) { - if (!txIn.scriptSig.IsZerocoinSpend()) - continue; - CoinSpend spend = TxInToZerocoinSpend(txIn); - int nHeightTx = 0; - if (IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTx)) - return state.Invalid(error("%s : zCcbc spend with serial %s is already in block %d\n", - __func__, spend.getCoinSerialNumber().GetHex(), nHeightTx)); - - //Is serial in the acceptable range - if (!spend.HasValidSerial(Params().Zerocoin_Params())) - return state.Invalid(error("%s : zCcbc spend with serial %s from tx %s is not in valid range\n", - __func__, spend.getCoinSerialNumber().GetHex(), tx.GetHash().GetHex())); - } - } - else { - LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); - 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). - for (const CTxIn txin : tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; - return false; - } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); - - // Bring the best block into scope - view.GetBestBlock(); - - nValueIn = view.GetValueIn(tx); - - // 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 (Params().RequireStandard() && !AreInputsStandard(tx, view)) - return error("AcceptToMemoryPool: : nonstandard transaction input"); - - // Check that the transaction doesn't have an excessive number of - // sigops, making it impossible to mine. Since the coinbase transaction - // itself can contain sigops MAX_TX_SIGOPS is less than - // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than - // merely non-standard transaction. - if (!tx.IsZerocoinSpend()) { - unsigned int nSigOps = GetLegacySigOpCount(tx); - unsigned int nMaxSigOps = MAX_TX_SIGOPS_CURRENT; - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > nMaxSigOps) - return state.DoS(0, - error("AcceptToMemoryPool : too many sigops %s, %d > %d", - hash.ToString(), nSigOps, nMaxSigOps), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); - } - - CAmount nValueOut = tx.GetValueOut(); - CAmount nFees = nValueIn - nValueOut; - double dPriority = 0; - if (!tx.IsZerocoinSpend()) - view.GetPriority(tx, chainActive.Height()); - - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); - unsigned int nSize = entry.GetTxSize(); - - // Don't accept it if it can't get into a block - // but prioritise dstx and don't check fees for it - if (mapObfuscationBroadcastTxes.count(hash)) { - mempool.PrioritiseTransaction(hash, hash.ToString(), 1000, 0.1 * COIN); - } - else if (!ignoreFees) { - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); - if (fLimitFree && nFees < txMinFee && !tx.IsZerocoinSpend()) - return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); - - // Require that free transactions have sufficient priority to be mined in the next block. - if (tx.IsZerocoinMint()) { - if (nFees < Params().Zerocoin_MintFee() * tx.GetZerocoinMintCount()) - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee for zerocoinmint"); - } - else if (!tx.IsZerocoinSpend() && GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); - } - - // Continuously rate-limit free (really, very-low-fee) 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 < ::minRelayTxFee.GetFee(nSize) && !tx.IsZerocoinSpend()) { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); - - // 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", 30) * 10 * 1000) - return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nSize); - dFreeCount += nSize; - } - } - - if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: : insane fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); - - // Check against previous transactions - // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); - } - - // Check again against just the consensus-critical mandatory script - // verification flags, in case of bugs in the standard flags that cause - // transactions to pass as valid when they're actually invalid. For - // instance the STRICTENC flag was incorrectly allowing certain - // CHECKSIG NOT scripts to pass, even though they were invalid. - // - // There is a similar check in CreateNewBlock() to prevent creating - // invalid blocks, however allowing such transactions into the mempool - // can be exploited as a DoS attack. - if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptToMemoryPool: : BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); - } - - // Store transaction in memory - pool.addUnchecked(hash, entry); - } - - SyncWithWallets(tx, NULL); + AssertLockHeld(cs_main); + if (pfMissingInputs) + *pfMissingInputs = false; + + //Temporarily disable zerocoin for maintenance + if (GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && tx.ContainsZerocoins()) + return state.DoS(10, error("AcceptToMemoryPool : Zerocoin transactions are temporarily disabled for maintenance"), REJECT_INVALID, "bad-tx"); + + if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_AccumulatorStartHeight(), true, state)) + return state.DoS(100, error("AcceptToMemoryPool: : CheckTransaction failed"), REJECT_INVALID, "bad-tx"); + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"), + REJECT_INVALID, "coinbase"); + + //Coinstake is also only valid in a block, not as a loose transaction + if (tx.IsCoinStake()) + return state.DoS(100, error("AcceptToMemoryPool: coinstake as individual tx"), + REJECT_INVALID, "coinstake"); + + // Rather not work on nonstandard transactions (unless -testnet/-regtest) + string reason; + if (Params().RequireStandard() && !IsStandardTx(tx, reason)) + return state.DoS(0, + error("AcceptToMemoryPool : nonstandard transaction: %s", reason), + REJECT_NONSTANDARD, reason); + // is it already in the memory pool? + uint256 hash = tx.GetHash(); + if (pool.exists(hash)) { + LogPrintf("%s tx already in mempool\n", __func__); + return false; + } + + // ----------- swiftTX transaction scanning ----------- + + BOOST_FOREACH (const CTxIn& in, tx.vin) { + if (mapLockedInputs.count(in.prevout)) { + if (mapLockedInputs[in.prevout] != tx.GetHash()) { + return state.DoS(0, + error("AcceptToMemoryPool : conflicts with existing transaction lock: %s", reason), + REJECT_INVALID, "tx-lock-conflict"); + } + } + } + + // Check for conflicts with in-memory transactions + if (!tx.IsZerocoinSpend()) { + LOCK(pool.cs); // protect pool.mapNextTx + for (unsigned int i = 0; i < tx.vin.size(); i++) { + COutPoint outpoint = tx.vin[i].prevout; + if (pool.mapNextTx.count(outpoint)) { + // Disable replacement feature for now + return false; + } + } + } + + + { + CCoinsView dummy; + CCoinsViewCache view(&dummy); + + CAmount nValueIn = 0; + if (tx.IsZerocoinSpend()) { + nValueIn = tx.GetZerocoinSpent(); + + //Check that txid is not already in the chain + int nHeightTx = 0; + if (IsTransactionInChain(tx.GetHash(), nHeightTx)) + return state.Invalid(error("AcceptToMemoryPool : zCcbc spend tx %s already in block %d", tx.GetHash().GetHex(), nHeightTx), + REJECT_DUPLICATE, "bad-txns-inputs-spent"); + + //Check for double spending of serial #'s + for (const CTxIn& txIn : tx.vin) { + if (!txIn.scriptSig.IsZerocoinSpend()) + continue; + CoinSpend spend = TxInToZerocoinSpend(txIn); + int nHeightTx = 0; + if (IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTx)) + return state.Invalid(error("%s : zCcbc spend with serial %s is already in block %d\n", + __func__, spend.getCoinSerialNumber().GetHex(), nHeightTx)); + + //Is serial in the acceptable range + if (!spend.HasValidSerial(Params().Zerocoin_Params())) + return state.Invalid(error("%s : zCcbc spend with serial %s from tx %s is not in valid range\n", + __func__, spend.getCoinSerialNumber().GetHex(), tx.GetHash().GetHex())); + } + } else { + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + 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). + for (const CTxIn txin : tx.vin) { + if (!view.HaveCoins(txin.prevout.hash)) { + if (pfMissingInputs) + *pfMissingInputs = true; + return false; + } + } - return true; + // are the actual inputs available? + if (!view.HaveInputs(tx)) + return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), + REJECT_DUPLICATE, "bad-txns-inputs-spent"); + + // Bring the best block into scope + view.GetBestBlock(); + + nValueIn = view.GetValueIn(tx); + + // 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 (Params().RequireStandard() && !AreInputsStandard(tx, view)) + return error("AcceptToMemoryPool: : nonstandard transaction input"); + + // Check that the transaction doesn't have an excessive number of + // sigops, making it impossible to mine. Since the coinbase transaction + // itself can contain sigops MAX_TX_SIGOPS is less than + // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than + // merely non-standard transaction. + if (!tx.IsZerocoinSpend()) { + unsigned int nSigOps = GetLegacySigOpCount(tx); + unsigned int nMaxSigOps = MAX_TX_SIGOPS_CURRENT; + nSigOps += GetP2SHSigOpCount(tx, view); + if (nSigOps > nMaxSigOps) + return state.DoS(0, + error("AcceptToMemoryPool : too many sigops %s, %d > %d", + hash.ToString(), nSigOps, nMaxSigOps), + REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + } + + CAmount nValueOut = tx.GetValueOut(); + CAmount nFees = nValueIn - nValueOut; + double dPriority = 0; + if (!tx.IsZerocoinSpend()) + view.GetPriority(tx, chainActive.Height()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + unsigned int nSize = entry.GetTxSize(); + + // Don't accept it if it can't get into a block + // but prioritise dstx and don't check fees for it + if (mapObfuscationBroadcastTxes.count(hash)) { + mempool.PrioritiseTransaction(hash, hash.ToString(), 1000, 0.1 * COIN); + } else if (!ignoreFees) { + CAmount txMinFee = GetMinRelayFee(tx, nSize, true); + if (fLimitFree && nFees < txMinFee && !tx.IsZerocoinSpend()) + return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); + + // Require that free transactions have sufficient priority to be mined in the next block. + if (tx.IsZerocoinMint()) { + if (nFees < Params().Zerocoin_MintFee() * tx.GetZerocoinMintCount()) + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee for zerocoinmint"); + } else if (!tx.IsZerocoinSpend() && GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); + } + + // Continuously rate-limit free (really, very-low-fee) 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 < ::minRelayTxFee.GetFee(nSize) && !tx.IsZerocoinSpend()) { + static CCriticalSection csFreeLimiter; + static double dFreeCount; + static int64_t nLastTime; + int64_t nNow = GetTime(); + + LOCK(csFreeLimiter); + + // 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", 30) * 10 * 1000) + return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), + REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nSize); + dFreeCount += nSize; + } + } + + if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) + return error("AcceptToMemoryPool: : insane fees %s, %d > %d", + hash.ToString(), + nFees, ::minRelayTxFee.GetFee(nSize) * 10000); + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); + } + + // Check again against just the consensus-critical mandatory script + // verification flags, in case of bugs in the standard flags that cause + // transactions to pass as valid when they're actually invalid. For + // instance the STRICTENC flag was incorrectly allowing certain + // CHECKSIG NOT scripts to pass, even though they were invalid. + // + // There is a similar check in CreateNewBlock() to prevent creating + // invalid blocks, however allowing such transactions into the mempool + // can be exploited as a DoS attack. + if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) { + return error("AcceptToMemoryPool: : BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + } + + // Store transaction in memory + pool.addUnchecked(hash, entry); + } + + SyncWithWallets(tx, NULL); + + return true; } bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee, bool isDSTX) { - AssertLockHeld(cs_main); - if (pfMissingInputs) - *pfMissingInputs = false; - - if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_AccumulatorStartHeight(), true, state)) - return error("AcceptableInputs: : CheckTransaction failed"); - - // Coinbase is only valid in a block, not as a loose transaction - if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptableInputs: : coinbase as individual tx"), - REJECT_INVALID, "coinbase"); - - // Rather not work on nonstandard transactions (unless -testnet/-regtest) - string reason; - // for any real tx this will be checked on AcceptToMemoryPool anyway - // if (Params().RequireStandard() && !IsStandardTx(tx, reason)) - // return state.DoS(0, - // error("AcceptableInputs : nonstandard transaction: %s", reason), - // REJECT_NONSTANDARD, reason); - - // is it already in the memory pool? - uint256 hash = tx.GetHash(); - if (pool.exists(hash)) - return false; - - // ----------- swiftTX transaction scanning ----------- - - BOOST_FOREACH(const CTxIn& in, tx.vin) { - if (mapLockedInputs.count(in.prevout)) { - if (mapLockedInputs[in.prevout] != tx.GetHash()) { - return state.DoS(0, - error("AcceptableInputs : conflicts with existing transaction lock: %s", reason), - REJECT_INVALID, "tx-lock-conflict"); - } - } - } - - // Check for conflicts with in-memory transactions - if (!tx.IsZerocoinSpend()) { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) { - // Disable replacement feature for now - return false; - } - } - } - - - { - CCoinsView dummy; - CCoinsViewCache view(&dummy); - - CAmount nValueIn = 0; - { - LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); - 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). - for (const CTxIn txin : tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; - return false; - } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptableInputs : inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); - - // Bring the best block into scope - view.GetBestBlock(); - - nValueIn = view.GetValueIn(tx); - - // 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 - // for any real tx this will be checked on AcceptToMemoryPool anyway - // if (Params().RequireStandard() && !AreInputsStandard(tx, view)) - // return error("AcceptableInputs: : nonstandard transaction input"); - - // Check that the transaction doesn't have an excessive number of - // sigops, making it impossible to mine. Since the coinbase transaction - // itself can contain sigops MAX_TX_SIGOPS is less than - // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than - // merely non-standard transaction. - unsigned int nSigOps = GetLegacySigOpCount(tx); - unsigned int nMaxSigOps = MAX_TX_SIGOPS_CURRENT; - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > nMaxSigOps) - return state.DoS(0, - error("AcceptableInputs : too many sigops %s, %d > %d", - hash.ToString(), nSigOps, nMaxSigOps), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); - - CAmount nValueOut = tx.GetValueOut(); - CAmount nFees = nValueIn - nValueOut; - double dPriority = view.GetPriority(tx, chainActive.Height()); - - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); - unsigned int nSize = entry.GetTxSize(); - - // Don't accept it if it can't get into a block - // but prioritise dstx and don't check fees for it - if (isDSTX) { - mempool.PrioritiseTransaction(hash, hash.ToString(), 1000, 0.1 * COIN); - } - else { // same as !ignoreFees for AcceptToMemoryPool - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); - if (fLimitFree && nFees < txMinFee && !tx.IsZerocoinSpend()) - return state.DoS(0, error("AcceptableInputs : not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); - - // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); - } - - // Continuously rate-limit free (really, very-low-fee) 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 < ::minRelayTxFee.GetFee(nSize) && !tx.IsZerocoinSpend()) { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); - - // 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", 30) * 10 * 1000) - return state.DoS(0, error("AcceptableInputs : free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nSize); - dFreeCount += nSize; - } - } - - if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptableInputs: : insane fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); - - // Check against previous transactions - // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, false, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptableInputs: : ConnectInputs failed %s", hash.ToString()); - } - - // Check again against just the consensus-critical mandatory script - // verification flags, in case of bugs in the standard flags that cause - // transactions to pass as valid when they're actually invalid. For - // instance the STRICTENC flag was incorrectly allowing certain - // CHECKSIG NOT scripts to pass, even though they were invalid. - // - // There is a similar check in CreateNewBlock() to prevent creating - // invalid blocks, however allowing such transactions into the mempool - // can be exploited as a DoS attack. - // for any real tx this will be checked on AcceptToMemoryPool anyway - // if (!CheckInputs(tx, state, view, false, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) - // { - // return error("AcceptableInputs: : BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); - // } - - // Store transaction in memory - // pool.addUnchecked(hash, entry); - } - - // SyncWithWallets(tx, NULL); + AssertLockHeld(cs_main); + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_AccumulatorStartHeight(), true, state)) + return error("AcceptableInputs: : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + return state.DoS(100, error("AcceptableInputs: : coinbase as individual tx"), + REJECT_INVALID, "coinbase"); + + // Rather not work on nonstandard transactions (unless -testnet/-regtest) + string reason; + // for any real tx this will be checked on AcceptToMemoryPool anyway + // if (Params().RequireStandard() && !IsStandardTx(tx, reason)) + // return state.DoS(0, + // error("AcceptableInputs : nonstandard transaction: %s", reason), + // REJECT_NONSTANDARD, reason); + + // is it already in the memory pool? + uint256 hash = tx.GetHash(); + if (pool.exists(hash)) + return false; + + // ----------- swiftTX transaction scanning ----------- + + BOOST_FOREACH (const CTxIn& in, tx.vin) { + if (mapLockedInputs.count(in.prevout)) { + if (mapLockedInputs[in.prevout] != tx.GetHash()) { + return state.DoS(0, + error("AcceptableInputs : conflicts with existing transaction lock: %s", reason), + REJECT_INVALID, "tx-lock-conflict"); + } + } + } + + // Check for conflicts with in-memory transactions + if (!tx.IsZerocoinSpend()) { + LOCK(pool.cs); // protect pool.mapNextTx + for (unsigned int i = 0; i < tx.vin.size(); i++) { + COutPoint outpoint = tx.vin[i].prevout; + if (pool.mapNextTx.count(outpoint)) { + // Disable replacement feature for now + return false; + } + } + } + + + { + CCoinsView dummy; + CCoinsViewCache view(&dummy); + + CAmount nValueIn = 0; + { + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + 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). + for (const CTxIn txin : tx.vin) { + if (!view.HaveCoins(txin.prevout.hash)) { + if (pfMissingInputs) + *pfMissingInputs = true; + return false; + } + } - return true; + // are the actual inputs available? + if (!view.HaveInputs(tx)) + return state.Invalid(error("AcceptableInputs : inputs already spent"), + REJECT_DUPLICATE, "bad-txns-inputs-spent"); + + // Bring the best block into scope + view.GetBestBlock(); + + nValueIn = view.GetValueIn(tx); + + // 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 + // for any real tx this will be checked on AcceptToMemoryPool anyway + // if (Params().RequireStandard() && !AreInputsStandard(tx, view)) + // return error("AcceptableInputs: : nonstandard transaction input"); + + // Check that the transaction doesn't have an excessive number of + // sigops, making it impossible to mine. Since the coinbase transaction + // itself can contain sigops MAX_TX_SIGOPS is less than + // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than + // merely non-standard transaction. + unsigned int nSigOps = GetLegacySigOpCount(tx); + unsigned int nMaxSigOps = MAX_TX_SIGOPS_CURRENT; + nSigOps += GetP2SHSigOpCount(tx, view); + if (nSigOps > nMaxSigOps) + return state.DoS(0, + error("AcceptableInputs : too many sigops %s, %d > %d", + hash.ToString(), nSigOps, nMaxSigOps), + REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + + CAmount nValueOut = tx.GetValueOut(); + CAmount nFees = nValueIn - nValueOut; + double dPriority = view.GetPriority(tx, chainActive.Height()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + unsigned int nSize = entry.GetTxSize(); + + // Don't accept it if it can't get into a block + // but prioritise dstx and don't check fees for it + if (isDSTX) { + mempool.PrioritiseTransaction(hash, hash.ToString(), 1000, 0.1 * COIN); + } else { // same as !ignoreFees for AcceptToMemoryPool + CAmount txMinFee = GetMinRelayFee(tx, nSize, true); + if (fLimitFree && nFees < txMinFee && !tx.IsZerocoinSpend()) + return state.DoS(0, error("AcceptableInputs : not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); + + // Require that free transactions have sufficient priority to be mined in the next block. + if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); + } + + // Continuously rate-limit free (really, very-low-fee) 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 < ::minRelayTxFee.GetFee(nSize) && !tx.IsZerocoinSpend()) { + static CCriticalSection csFreeLimiter; + static double dFreeCount; + static int64_t nLastTime; + int64_t nNow = GetTime(); + + LOCK(csFreeLimiter); + + // 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", 30) * 10 * 1000) + return state.DoS(0, error("AcceptableInputs : free transaction rejected by rate limiter"), + REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nSize); + dFreeCount += nSize; + } + } + + if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) + return error("AcceptableInputs: : insane fees %s, %d > %d", + hash.ToString(), + nFees, ::minRelayTxFee.GetFee(nSize) * 10000); + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!CheckInputs(tx, state, view, false, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { + return error("AcceptableInputs: : ConnectInputs failed %s", hash.ToString()); + } + + // Check again against just the consensus-critical mandatory script + // verification flags, in case of bugs in the standard flags that cause + // transactions to pass as valid when they're actually invalid. For + // instance the STRICTENC flag was incorrectly allowing certain + // CHECKSIG NOT scripts to pass, even though they were invalid. + // + // There is a similar check in CreateNewBlock() to prevent creating + // invalid blocks, however allowing such transactions into the mempool + // can be exploited as a DoS attack. + // for any real tx this will be checked on AcceptToMemoryPool anyway + // if (!CheckInputs(tx, state, view, false, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) + // { + // return error("AcceptableInputs: : BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + // } + + // Store transaction in memory + // pool.addUnchecked(hash, entry); + } + + // SyncWithWallets(tx, NULL); + + return true; } /** 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); - { - if (mempool.lookup(hash, txOut)) { - return true; - } - } - - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } - catch (std::exception& e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); - } - hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) - return error("%s : txid mismatch", __func__); - return true; - } - } - - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - int nHeight = -1; - { - CCoinsViewCache& view = *pcoinsTip; - const CCoins* coins = view.AccessCoins(hash); - if (coins) - nHeight = coins->nHeight; - } - if (nHeight > 0) - pindexSlow = chainActive[nHeight]; - } - } - - if (pindexSlow) { - CBlock block; - if (ReadBlockFromDisk(block, pindexSlow)) { - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - if (tx.GetHash() == hash) { - txOut = tx; - hashBlock = pindexSlow->GetBlockHash(); - return true; - } - } - } - } + CBlockIndex* pindexSlow = NULL; + { + LOCK(cs_main); + { + if (mempool.lookup(hash, txOut)) { + return true; + } + } + + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (std::exception& e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s : txid mismatch", __func__); + return true; + } + } + + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache& view = *pcoinsTip; + const CCoins* coins = view.AccessCoins(hash); + if (coins) + nHeight = coins->nHeight; + } + if (nHeight > 0) + pindexSlow = chainActive[nHeight]; + } + } + + if (pindexSlow) { + CBlock block; + if (ReadBlockFromDisk(block, pindexSlow)) { + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + if (tx.GetHash() == hash) { + txOut = tx; + hashBlock = pindexSlow->GetBlockHash(); + return true; + } + } + } + } - return false; + return false; } @@ -2050,540 +2036,433 @@ bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) { - // Open history file to append - CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("WriteBlockToDisk : OpenBlockFile failed"); + // Open history file to append + CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("WriteBlockToDisk : OpenBlockFile failed"); - // Write index header - unsigned int nSize = fileout.GetSerializeSize(block); - fileout << FLATDATA(Params().MessageStart()) << nSize; + // Write index header + unsigned int nSize = fileout.GetSerializeSize(block); + fileout << FLATDATA(Params().MessageStart()) << nSize; - // Write block - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("WriteBlockToDisk : ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << block; + // Write block + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("WriteBlockToDisk : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << block; - return true; + return true; } bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) { - block.SetNull(); + block.SetNull(); - // Open history file to read - CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("ReadBlockFromDisk : OpenBlockFile failed"); + // Open history file to read + CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("ReadBlockFromDisk : OpenBlockFile failed"); - // Read block - try { - filein >> block; - } - catch (std::exception& e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); - } + // Read block + try { + filein >> block; + } catch (std::exception& e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } - // Check the header - if (block.IsProofOfWork()) { - if (!CheckProofOfWork(block.GetHash(), block.nBits)) - return error("ReadBlockFromDisk : Errors in block header"); - } + // Check the header + if (block.IsProofOfWork()) { + if (!CheckProofOfWork(block.GetHash(), block.nBits)) + return error("ReadBlockFromDisk : Errors in block header"); + } - return true; + return true; } bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) { - if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) - return false; - if (block.GetHash() != pindex->GetBlockHash()) { - LogPrintf("%s : block=%s index=%s\n", __func__, block.GetHash().ToString().c_str(), pindex->GetBlockHash().ToString().c_str()); - return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); - } - return true; + if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) + return false; + if (block.GetHash() != pindex->GetBlockHash()) { + LogPrintf("%s : block=%s index=%s\n", __func__, block.GetHash().ToString().c_str(), pindex->GetBlockHash().ToString().c_str()); + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); + } + return true; } double ConvertBitsToDouble(unsigned int nBits) { - int nShift = (nBits >> 24) & 0xff; + int nShift = (nBits >> 24) & 0xff; - double dDiff = - (double)0x0000ffff / (double)(nBits & 0x00ffffff); + double dDiff = + (double)0x0000ffff / (double)(nBits & 0x00ffffff); - while (nShift < 29) { - dDiff *= 256.0; - nShift++; - } - while (nShift > 29) { - dDiff /= 256.0; - nShift--; - } + while (nShift < 29) { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) { + dDiff /= 256.0; + nShift--; + } - return dDiff; + return dDiff; } int64_t GetBlockValue(int nHeight) { - int64_t nSubsidy = 0; - - if (Params().NetworkID() == CBaseChainParams::TESTNET) { - if (nHeight <= 200 && nHeight > 0) - return 250000 * COIN; - } - if (IsTreasuryBlock(nHeight)) { - LogPrintf("GetBlockValue(): this is a treasury block\n"); - nSubsidy = GetTreasuryAward(nHeight); - } - else if (IsReviveBlock(nHeight)) { - LogPrintf("GetBlockValue(): this is a revive block\n"); - nSubsidy = GetReviveAward(nHeight); - } + int64_t nSubsidy = 0; - else { - if (nHeight == 0) { - nSubsidy = 1600000 * COIN; - } else if (nHeight <= 5 && nHeight > 1) { //First POW phase - nSubsidy = 1600000 * COIN; - } else if (nHeight <= 200 && nHeight > 1) { //First POW phase - nSubsidy = 0 * COIN; - } else if (nHeight <= 25000 && nHeight > 200) { //Public phase 17.22 days 24,800 coins - nSubsidy = 1 * COIN; - } else if (nHeight <= 50000 && nHeight > 25000) { //17.36 days 625,000 coins - nSubsidy = 25 * COIN; - } else if (nHeight <= 75000 && nHeight > 50000) { //17.36 days 1,250,000 coins - nSubsidy = 50 * COIN; - } else if (nHeight <= 100000 && nHeight > 75000) { //17.36 days 2,125,000 coins - nSubsidy = 85 * COIN; - } else if (nHeight <= 125000 && nHeight > 100000) { //17.36 days 1,875,000 coins - nSubsidy = 75 * COIN; - } else if (nHeight <= 168000 && nHeight > 125000) { //30 days 2,150,000 coins - nSubsidy = 50 * COIN; - } else if (nHeight <= 297600 && nHeight > 168000) { //90 days 3,240,000 coins - nSubsidy = 25 * COIN; - } else if (nHeight <= 556800 && nHeight > 297600) { //180 days 2,592,000 coins - nSubsidy = 10 * COIN; - } else if (nHeight > 556800) { //Till max supply Total coins used 17,882,000 - nSubsidy = 5 * COIN; //57,026.38 days will max supply is reached - } + if (Params().NetworkID() == CBaseChainParams::TESTNET) { + if (nHeight <= 200 && nHeight > 0) + return 250000 * COIN; + } + if (IsTreasuryBlock(nHeight)) { + LogPrintf("GetBlockValue(): this is a treasury block\n"); + nSubsidy = GetTreasuryAward(nHeight); + } else if (IsReviveBlock(nHeight)) { + LogPrintf("GetBlockValue(): this is a revive block\n"); + nSubsidy = GetReviveAward(nHeight); + } - int64_t nMoneySupply = chainActive.Tip()->nMoneySupply; - if (nMoneySupply + nSubsidy >= Params().MaxMoneyOut()) - nSubsidy = Params().MaxMoneyOut() - nMoneySupply; - if (nMoneySupply >= Params().MaxMoneyOut()) - nSubsidy = 0; //Amount each block pays after max supply is reached - } - return nSubsidy; + else { + if (nHeight == 0) { + nSubsidy = 1600000 * COIN; + } else if (nHeight <= 5 && nHeight > 1) { //First POW phase + nSubsidy = 1600000 * COIN; + } else if (nHeight <= 200 && nHeight > 1) { //First POW phase + nSubsidy = 0 * COIN; + } else if (nHeight <= 25000 && nHeight > 200) { //Public phase 17.22 days 24,800 coins + nSubsidy = 1 * COIN; + } else if (nHeight <= 50000 && nHeight > 25000) { //17.36 days 625,000 coins + nSubsidy = 25 * COIN; + } else if (nHeight <= 75000 && nHeight > 50000) { //17.36 days 1,250,000 coins + nSubsidy = 50 * COIN; + } else if (nHeight <= 100000 && nHeight > 75000) { //17.36 days 2,125,000 coins + nSubsidy = 85 * COIN; + } else if (nHeight <= 125000 && nHeight > 100000) { //17.36 days 1,875,000 coins + nSubsidy = 75 * COIN; + } else if (nHeight <= 168000 && nHeight > 125000) { //30 days 2,150,000 coins + nSubsidy = 50 * COIN; + } else if (nHeight <= 297600 && nHeight > 168000) { //90 days 3,240,000 coins + nSubsidy = 25 * COIN; + } else if (nHeight <= 556800 && nHeight > 297600) { //180 days 2,592,000 coins + nSubsidy = 10 * COIN; + } else if (nHeight <= 556800) { //Till max supply Total coins used 17,882,000 + nSubsidy = 5 * COIN; //57,026.38 days will max supply is reached + } + + int64_t nMoneySupply = chainActive.Tip()->nMoneySupply; + if (nMoneySupply + nSubsidy >= Params().MaxMoneyOut()) + nSubsidy = Params().MaxMoneyOut() - nMoneySupply; + if (nMoneySupply >= Params().MaxMoneyOut()) + nSubsidy = 0; //Amount each block pays after max supply is reached + } + return nSubsidy; } -/* CAmount GetSeeSaw(const CAmount& blockValue, int nMasternodeCount, int nHeight) { - //if a mn count is inserted into the function we are looking for a specific result for a masternode count - if (nMasternodeCount < 1) { - if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) - nMasternodeCount = mnodeman.stable_size(); - else - nMasternodeCount = mnodeman.size(); - } - - int64_t nMoneySupply = chainActive.Tip()->nMoneySupply; - int64_t mNodeCoins = nMasternodeCount * 25000 * COIN; //25k coins required - - // Use this log to compare the masternode count for different clients - //LogPrintf("Adjusting seesaw at height %d with %d masternodes (without drift: %d) at %ld\n", nHeight, nMasternodeCount, nMasternodeCount - Params().MasternodeCountDrift(), GetTime()); - - if (fDebug) - LogPrintf("GetMasternodePayment(): moneysupply=%s, nodecoins=%s \n", FormatMoney(nMoneySupply).c_str(), - FormatMoney(mNodeCoins).c_str()); - - CAmount ret = 0; - if (mNodeCoins == 0) { - ret = 0; - } - else if (nHeight <= 250000) { - if (mNodeCoins <= (nMoneySupply * .05) && mNodeCoins > 0) { - ret = blockValue * .85; - } - else if (mNodeCoins <= (nMoneySupply * .1) && mNodeCoins > (nMoneySupply * .05)) { - ret = blockValue * .8; - } - else if (mNodeCoins <= (nMoneySupply * .15) && mNodeCoins > (nMoneySupply * .1)) { - ret = blockValue * .75; - } - else if (mNodeCoins <= (nMoneySupply * .2) && mNodeCoins > (nMoneySupply * .15)) { - ret = blockValue * .7; - } - else if (mNodeCoins <= (nMoneySupply * .25) && mNodeCoins > (nMoneySupply * .2)) { - ret = blockValue * .65; - } - else if (mNodeCoins <= (nMoneySupply * .3) && mNodeCoins > (nMoneySupply * .25)) { - ret = blockValue * .6; - } - else if (mNodeCoins <= (nMoneySupply * .35) && mNodeCoins > (nMoneySupply * .3)) { - ret = blockValue * .55; - } - else if (mNodeCoins <= (nMoneySupply * .4) && mNodeCoins > (nMoneySupply * .35)) { - ret = blockValue * .5; - } - else if (mNodeCoins <= (nMoneySupply * .45) && mNodeCoins > (nMoneySupply * .4)) { - ret = blockValue * .45; - } - else if (mNodeCoins <= (nMoneySupply * .5) && mNodeCoins > (nMoneySupply * .45)) { - ret = blockValue * .4; - } - else if (mNodeCoins <= (nMoneySupply * .55) && mNodeCoins > (nMoneySupply * .5)) { - ret = blockValue * .35; - } - else if (mNodeCoins <= (nMoneySupply * .6) && mNodeCoins > (nMoneySupply * .55)) { - ret = blockValue * .3; - } - else if (mNodeCoins <= (nMoneySupply * .65) && mNodeCoins > (nMoneySupply * .6)) { - ret = blockValue * .25; - } - else if (mNodeCoins <= (nMoneySupply * .7) && mNodeCoins > (nMoneySupply * .65)) { - ret = blockValue * .2; - } - else if (mNodeCoins <= (nMoneySupply * .75) && mNodeCoins > (nMoneySupply * .7)) { - ret = blockValue * .15; - } - else { - ret = blockValue * .1; - } - } - else if (nHeight > 250000) { - if (mNodeCoins <= (nMoneySupply * .01) && mNodeCoins > 0) { - ret = blockValue * .90; - } - else if (mNodeCoins <= (nMoneySupply * .02) && mNodeCoins > (nMoneySupply * .01)) { - ret = blockValue * .88; - } - else if (mNodeCoins <= (nMoneySupply * .03) && mNodeCoins > (nMoneySupply * .02)) { - ret = blockValue * .87; - } - else if (mNodeCoins <= (nMoneySupply * .04) && mNodeCoins > (nMoneySupply * .03)) { - ret = blockValue * .86; - } - else if (mNodeCoins <= (nMoneySupply * .05) && mNodeCoins > (nMoneySupply * .04)) { - ret = blockValue * .85; - } - else if (mNodeCoins <= (nMoneySupply * .06) && mNodeCoins > (nMoneySupply * .05)) { - ret = blockValue * .84; - } - else if (mNodeCoins <= (nMoneySupply * .07) && mNodeCoins > (nMoneySupply * .06)) { - ret = blockValue * .83; - } - else if (mNodeCoins <= (nMoneySupply * .08) && mNodeCoins > (nMoneySupply * .07)) { - ret = blockValue * .82; - } - else if (mNodeCoins <= (nMoneySupply * .09) && mNodeCoins > (nMoneySupply * .08)) { - ret = blockValue * .81; - } - else if (mNodeCoins <= (nMoneySupply * .10) && mNodeCoins > (nMoneySupply * .09)) { - ret = blockValue * .80; - } - else if (mNodeCoins <= (nMoneySupply * .11) && mNodeCoins > (nMoneySupply * .10)) { - ret = blockValue * .79; - } - else if (mNodeCoins <= (nMoneySupply * .12) && mNodeCoins > (nMoneySupply * .11)) { - ret = blockValue * .78; - } - else if (mNodeCoins <= (nMoneySupply * .13) && mNodeCoins > (nMoneySupply * .12)) { - ret = blockValue * .77; - } - else if (mNodeCoins <= (nMoneySupply * .14) && mNodeCoins > (nMoneySupply * .13)) { - ret = blockValue * .76; - } - else if (mNodeCoins <= (nMoneySupply * .15) && mNodeCoins > (nMoneySupply * .14)) { - ret = blockValue * .75; - } - else if (mNodeCoins <= (nMoneySupply * .16) && mNodeCoins > (nMoneySupply * .15)) { - ret = blockValue * .74; - } - else if (mNodeCoins <= (nMoneySupply * .17) && mNodeCoins > (nMoneySupply * .16)) { - ret = blockValue * .73; - } - else if (mNodeCoins <= (nMoneySupply * .18) && mNodeCoins > (nMoneySupply * .17)) { - ret = blockValue * .72; - } - else if (mNodeCoins <= (nMoneySupply * .19) && mNodeCoins > (nMoneySupply * .18)) { - ret = blockValue * .71; - } - else if (mNodeCoins <= (nMoneySupply * .20) && mNodeCoins > (nMoneySupply * .19)) { - ret = blockValue * .70; - } - else if (mNodeCoins <= (nMoneySupply * .21) && mNodeCoins > (nMoneySupply * .20)) { - ret = blockValue * .69; - } - else if (mNodeCoins <= (nMoneySupply * .22) && mNodeCoins > (nMoneySupply * .21)) { - ret = blockValue * .68; - } - else if (mNodeCoins <= (nMoneySupply * .23) && mNodeCoins > (nMoneySupply * .22)) { - ret = blockValue * .67; - } - else if (mNodeCoins <= (nMoneySupply * .24) && mNodeCoins > (nMoneySupply * .23)) { - ret = blockValue * .66; - } - else if (mNodeCoins <= (nMoneySupply * .25) && mNodeCoins > (nMoneySupply * .24)) { - ret = blockValue * .65; - } - else if (mNodeCoins <= (nMoneySupply * .26) && mNodeCoins > (nMoneySupply * .25)) { - ret = blockValue * .64; - } - else if (mNodeCoins <= (nMoneySupply * .27) && mNodeCoins > (nMoneySupply * .26)) { - ret = blockValue * .63; - } - else if (mNodeCoins <= (nMoneySupply * .28) && mNodeCoins > (nMoneySupply * .27)) { - ret = blockValue * .62; - } - else if (mNodeCoins <= (nMoneySupply * .29) && mNodeCoins > (nMoneySupply * .28)) { - ret = blockValue * .61; - } - else if (mNodeCoins <= (nMoneySupply * .30) && mNodeCoins > (nMoneySupply * .29)) { - ret = blockValue * .60; - } - else if (mNodeCoins <= (nMoneySupply * .31) && mNodeCoins > (nMoneySupply * .30)) { - ret = blockValue * .59; - } - else if (mNodeCoins <= (nMoneySupply * .32) && mNodeCoins > (nMoneySupply * .31)) { - ret = blockValue * .58; - } - else if (mNodeCoins <= (nMoneySupply * .33) && mNodeCoins > (nMoneySupply * .32)) { - ret = blockValue * .57; - } - else if (mNodeCoins <= (nMoneySupply * .34) && mNodeCoins > (nMoneySupply * .33)) { - ret = blockValue * .56; - } - else if (mNodeCoins <= (nMoneySupply * .35) && mNodeCoins > (nMoneySupply * .34)) { - ret = blockValue * .55; - } - else if (mNodeCoins <= (nMoneySupply * .363) && mNodeCoins > (nMoneySupply * .35)) { - ret = blockValue * .54; - } - else if (mNodeCoins <= (nMoneySupply * .376) && mNodeCoins > (nMoneySupply * .363)) { - ret = blockValue * .53; - } - else if (mNodeCoins <= (nMoneySupply * .389) && mNodeCoins > (nMoneySupply * .376)) { - ret = blockValue * .52; - } - else if (mNodeCoins <= (nMoneySupply * .402) && mNodeCoins > (nMoneySupply * .389)) { - ret = blockValue * .51; - } - else if (mNodeCoins <= (nMoneySupply * .415) && mNodeCoins > (nMoneySupply * .402)) { - ret = blockValue * .50; - } - else if (mNodeCoins <= (nMoneySupply * .428) && mNodeCoins > (nMoneySupply * .415)) { - ret = blockValue * .49; - } - else if (mNodeCoins <= (nMoneySupply * .441) && mNodeCoins > (nMoneySupply * .428)) { - ret = blockValue * .48; - } - else if (mNodeCoins <= (nMoneySupply * .454) && mNodeCoins > (nMoneySupply * .441)) { - ret = blockValue * .47; - } - else if (mNodeCoins <= (nMoneySupply * .467) && mNodeCoins > (nMoneySupply * .454)) { - ret = blockValue * .46; - } - else if (mNodeCoins <= (nMoneySupply * .48) && mNodeCoins > (nMoneySupply * .467)) { - ret = blockValue * .45; - } - else if (mNodeCoins <= (nMoneySupply * .493) && mNodeCoins > (nMoneySupply * .48)) { - ret = blockValue * .44; - } - else if (mNodeCoins <= (nMoneySupply * .506) && mNodeCoins > (nMoneySupply * .493)) { - ret = blockValue * .43; - } - else if (mNodeCoins <= (nMoneySupply * .519) && mNodeCoins > (nMoneySupply * .506)) { - ret = blockValue * .42; - } - else if (mNodeCoins <= (nMoneySupply * .532) && mNodeCoins > (nMoneySupply * .519)) { - ret = blockValue * .41; - } - else if (mNodeCoins <= (nMoneySupply * .545) && mNodeCoins > (nMoneySupply * .532)) { - ret = blockValue * .40; - } - else if (mNodeCoins <= (nMoneySupply * .558) && mNodeCoins > (nMoneySupply * .545)) { - ret = blockValue * .39; - } - else if (mNodeCoins <= (nMoneySupply * .571) && mNodeCoins > (nMoneySupply * .558)) { - ret = blockValue * .38; - } - else if (mNodeCoins <= (nMoneySupply * .584) && mNodeCoins > (nMoneySupply * .571)) { - ret = blockValue * .37; - } - else if (mNodeCoins <= (nMoneySupply * .597) && mNodeCoins > (nMoneySupply * .584)) { - ret = blockValue * .36; - } - else if (mNodeCoins <= (nMoneySupply * .61) && mNodeCoins > (nMoneySupply * .597)) { - ret = blockValue * .35; - } - else if (mNodeCoins <= (nMoneySupply * .623) && mNodeCoins > (nMoneySupply * .61)) { - ret = blockValue * .34; - } - else if (mNodeCoins <= (nMoneySupply * .636) && mNodeCoins > (nMoneySupply * .623)) { - ret = blockValue * .33; - } - else if (mNodeCoins <= (nMoneySupply * .649) && mNodeCoins > (nMoneySupply * .636)) { - ret = blockValue * .32; - } - else if (mNodeCoins <= (nMoneySupply * .662) && mNodeCoins > (nMoneySupply * .649)) { - ret = blockValue * .31; - } - else if (mNodeCoins <= (nMoneySupply * .675) && mNodeCoins > (nMoneySupply * .662)) { - ret = blockValue * .30; - } - else if (mNodeCoins <= (nMoneySupply * .688) && mNodeCoins > (nMoneySupply * .675)) { - ret = blockValue * .29; - } - else if (mNodeCoins <= (nMoneySupply * .701) && mNodeCoins > (nMoneySupply * .688)) { - ret = blockValue * .28; - } - else if (mNodeCoins <= (nMoneySupply * .714) && mNodeCoins > (nMoneySupply * .701)) { - ret = blockValue * .27; - } - else if (mNodeCoins <= (nMoneySupply * .727) && mNodeCoins > (nMoneySupply * .714)) { - ret = blockValue * .26; - } - else if (mNodeCoins <= (nMoneySupply * .74) && mNodeCoins > (nMoneySupply * .727)) { - ret = blockValue * .25; - } - else if (mNodeCoins <= (nMoneySupply * .753) && mNodeCoins > (nMoneySupply * .74)) { - ret = blockValue * .24; - } - else if (mNodeCoins <= (nMoneySupply * .766) && mNodeCoins > (nMoneySupply * .753)) { - ret = blockValue * .23; - } - else if (mNodeCoins <= (nMoneySupply * .779) && mNodeCoins > (nMoneySupply * .766)) { - ret = blockValue * .22; - } - else if (mNodeCoins <= (nMoneySupply * .792) && mNodeCoins > (nMoneySupply * .779)) { - ret = blockValue * .21; - } - else if (mNodeCoins <= (nMoneySupply * .805) && mNodeCoins > (nMoneySupply * .792)) { - ret = blockValue * .20; - } - else if (mNodeCoins <= (nMoneySupply * .818) && mNodeCoins > (nMoneySupply * .805)) { - ret = blockValue * .19; - } - else if (mNodeCoins <= (nMoneySupply * .831) && mNodeCoins > (nMoneySupply * .818)) { - ret = blockValue * .18; - } - else if (mNodeCoins <= (nMoneySupply * .844) && mNodeCoins > (nMoneySupply * .831)) { - ret = blockValue * .17; - } - else if (mNodeCoins <= (nMoneySupply * .857) && mNodeCoins > (nMoneySupply * .844)) { - ret = blockValue * .16; - } - else if (mNodeCoins <= (nMoneySupply * .87) && mNodeCoins > (nMoneySupply * .857)) { - ret = blockValue * .15; - } - else if (mNodeCoins <= (nMoneySupply * .883) && mNodeCoins > (nMoneySupply * .87)) { - ret = blockValue * .14; - } - else if (mNodeCoins <= (nMoneySupply * .896) && mNodeCoins > (nMoneySupply * .883)) { - ret = blockValue * .13; - } - else if (mNodeCoins <= (nMoneySupply * .909) && mNodeCoins > (nMoneySupply * .896)) { - ret = blockValue * .12; - } - else if (mNodeCoins <= (nMoneySupply * .922) && mNodeCoins > (nMoneySupply * .909)) { - ret = blockValue * .11; - } - else if (mNodeCoins <= (nMoneySupply * .935) && mNodeCoins > (nMoneySupply * .922)) { - ret = blockValue * .10; - } - else if (mNodeCoins <= (nMoneySupply * .945) && mNodeCoins > (nMoneySupply * .935)) { - ret = blockValue * .09; - } - else if (mNodeCoins <= (nMoneySupply * .961) && mNodeCoins > (nMoneySupply * .945)) { - ret = blockValue * .08; - } - else if (mNodeCoins <= (nMoneySupply * .974) && mNodeCoins > (nMoneySupply * .961)) { - ret = blockValue * .07; - } - else if (mNodeCoins <= (nMoneySupply * .987) && mNodeCoins > (nMoneySupply * .974)) { - ret = blockValue * .06; - } - else if (mNodeCoins <= (nMoneySupply * .99) && mNodeCoins > (nMoneySupply * .987)) { - ret = blockValue * .05; - } - else { - ret = blockValue * .01; - } - } - return ret; + //if a mn count is inserted into the function we are looking for a specific result for a masternode count + if (nMasternodeCount < 1) { + if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) + nMasternodeCount = mnodeman.stable_size(); + else + nMasternodeCount = mnodeman.size(); + } + + int64_t nMoneySupply = chainActive.Tip()->nMoneySupply; + int64_t mNodeCoins = nMasternodeCount * 25000 * COIN; //25k coins required + + // Use this log to compare the masternode count for different clients + //LogPrintf("Adjusting seesaw at height %d with %d masternodes (without drift: %d) at %ld\n", nHeight, nMasternodeCount, nMasternodeCount - Params().MasternodeCountDrift(), GetTime()); + + if (fDebug) + LogPrintf("GetMasternodePayment(): moneysupply=%s, nodecoins=%s \n", FormatMoney(nMoneySupply).c_str(), + FormatMoney(mNodeCoins).c_str()); + + CAmount ret = 0; + if (mNodeCoins == 0) { + ret = 0; + } else if (nHeight <= 250000) { + if (mNodeCoins <= (nMoneySupply * .05) && mNodeCoins > 0) { + ret = blockValue * .85; + } else if (mNodeCoins <= (nMoneySupply * .1) && mNodeCoins > (nMoneySupply * .05)) { + ret = blockValue * .8; + } else if (mNodeCoins <= (nMoneySupply * .15) && mNodeCoins > (nMoneySupply * .1)) { + ret = blockValue * .75; + } else if (mNodeCoins <= (nMoneySupply * .2) && mNodeCoins > (nMoneySupply * .15)) { + ret = blockValue * .7; + } else if (mNodeCoins <= (nMoneySupply * .25) && mNodeCoins > (nMoneySupply * .2)) { + ret = blockValue * .65; + } else if (mNodeCoins <= (nMoneySupply * .3) && mNodeCoins > (nMoneySupply * .25)) { + ret = blockValue * .6; + } else if (mNodeCoins <= (nMoneySupply * .35) && mNodeCoins > (nMoneySupply * .3)) { + ret = blockValue * .55; + } else if (mNodeCoins <= (nMoneySupply * .4) && mNodeCoins > (nMoneySupply * .35)) { + ret = blockValue * .5; + } else if (mNodeCoins <= (nMoneySupply * .45) && mNodeCoins > (nMoneySupply * .4)) { + ret = blockValue * .45; + } else if (mNodeCoins <= (nMoneySupply * .5) && mNodeCoins > (nMoneySupply * .45)) { + ret = blockValue * .4; + } else if (mNodeCoins <= (nMoneySupply * .55) && mNodeCoins > (nMoneySupply * .5)) { + ret = blockValue * .35; + } else if (mNodeCoins <= (nMoneySupply * .6) && mNodeCoins > (nMoneySupply * .55)) { + ret = blockValue * .3; + } else if (mNodeCoins <= (nMoneySupply * .65) && mNodeCoins > (nMoneySupply * .6)) { + ret = blockValue * .25; + } else if (mNodeCoins <= (nMoneySupply * .7) && mNodeCoins > (nMoneySupply * .65)) { + ret = blockValue * .2; + } else if (mNodeCoins <= (nMoneySupply * .75) && mNodeCoins > (nMoneySupply * .7)) { + ret = blockValue * .15; + } else { + ret = blockValue * .1; + } + } else if (nHeight > 250000) { + if (mNodeCoins <= (nMoneySupply * .01) && mNodeCoins > 0) { + ret = blockValue * .90; + } else if (mNodeCoins <= (nMoneySupply * .02) && mNodeCoins > (nMoneySupply * .01)) { + ret = blockValue * .88; + } else if (mNodeCoins <= (nMoneySupply * .03) && mNodeCoins > (nMoneySupply * .02)) { + ret = blockValue * .87; + } else if (mNodeCoins <= (nMoneySupply * .04) && mNodeCoins > (nMoneySupply * .03)) { + ret = blockValue * .86; + } else if (mNodeCoins <= (nMoneySupply * .05) && mNodeCoins > (nMoneySupply * .04)) { + ret = blockValue * .85; + } else if (mNodeCoins <= (nMoneySupply * .06) && mNodeCoins > (nMoneySupply * .05)) { + ret = blockValue * .84; + } else if (mNodeCoins <= (nMoneySupply * .07) && mNodeCoins > (nMoneySupply * .06)) { + ret = blockValue * .83; + } else if (mNodeCoins <= (nMoneySupply * .08) && mNodeCoins > (nMoneySupply * .07)) { + ret = blockValue * .82; + } else if (mNodeCoins <= (nMoneySupply * .09) && mNodeCoins > (nMoneySupply * .08)) { + ret = blockValue * .81; + } else if (mNodeCoins <= (nMoneySupply * .10) && mNodeCoins > (nMoneySupply * .09)) { + ret = blockValue * .80; + } else if (mNodeCoins <= (nMoneySupply * .11) && mNodeCoins > (nMoneySupply * .10)) { + ret = blockValue * .79; + } else if (mNodeCoins <= (nMoneySupply * .12) && mNodeCoins > (nMoneySupply * .11)) { + ret = blockValue * .78; + } else if (mNodeCoins <= (nMoneySupply * .13) && mNodeCoins > (nMoneySupply * .12)) { + ret = blockValue * .77; + } else if (mNodeCoins <= (nMoneySupply * .14) && mNodeCoins > (nMoneySupply * .13)) { + ret = blockValue * .76; + } else if (mNodeCoins <= (nMoneySupply * .15) && mNodeCoins > (nMoneySupply * .14)) { + ret = blockValue * .75; + } else if (mNodeCoins <= (nMoneySupply * .16) && mNodeCoins > (nMoneySupply * .15)) { + ret = blockValue * .74; + } else if (mNodeCoins <= (nMoneySupply * .17) && mNodeCoins > (nMoneySupply * .16)) { + ret = blockValue * .73; + } else if (mNodeCoins <= (nMoneySupply * .18) && mNodeCoins > (nMoneySupply * .17)) { + ret = blockValue * .72; + } else if (mNodeCoins <= (nMoneySupply * .19) && mNodeCoins > (nMoneySupply * .18)) { + ret = blockValue * .71; + } else if (mNodeCoins <= (nMoneySupply * .20) && mNodeCoins > (nMoneySupply * .19)) { + ret = blockValue * .70; + } else if (mNodeCoins <= (nMoneySupply * .21) && mNodeCoins > (nMoneySupply * .20)) { + ret = blockValue * .69; + } else if (mNodeCoins <= (nMoneySupply * .22) && mNodeCoins > (nMoneySupply * .21)) { + ret = blockValue * .68; + } else if (mNodeCoins <= (nMoneySupply * .23) && mNodeCoins > (nMoneySupply * .22)) { + ret = blockValue * .67; + } else if (mNodeCoins <= (nMoneySupply * .24) && mNodeCoins > (nMoneySupply * .23)) { + ret = blockValue * .66; + } else if (mNodeCoins <= (nMoneySupply * .25) && mNodeCoins > (nMoneySupply * .24)) { + ret = blockValue * .65; + } else if (mNodeCoins <= (nMoneySupply * .26) && mNodeCoins > (nMoneySupply * .25)) { + ret = blockValue * .64; + } else if (mNodeCoins <= (nMoneySupply * .27) && mNodeCoins > (nMoneySupply * .26)) { + ret = blockValue * .63; + } else if (mNodeCoins <= (nMoneySupply * .28) && mNodeCoins > (nMoneySupply * .27)) { + ret = blockValue * .62; + } else if (mNodeCoins <= (nMoneySupply * .29) && mNodeCoins > (nMoneySupply * .28)) { + ret = blockValue * .61; + } else if (mNodeCoins <= (nMoneySupply * .30) && mNodeCoins > (nMoneySupply * .29)) { + ret = blockValue * .60; + } else if (mNodeCoins <= (nMoneySupply * .31) && mNodeCoins > (nMoneySupply * .30)) { + ret = blockValue * .59; + } else if (mNodeCoins <= (nMoneySupply * .32) && mNodeCoins > (nMoneySupply * .31)) { + ret = blockValue * .58; + } else if (mNodeCoins <= (nMoneySupply * .33) && mNodeCoins > (nMoneySupply * .32)) { + ret = blockValue * .57; + } else if (mNodeCoins <= (nMoneySupply * .34) && mNodeCoins > (nMoneySupply * .33)) { + ret = blockValue * .56; + } else if (mNodeCoins <= (nMoneySupply * .35) && mNodeCoins > (nMoneySupply * .34)) { + ret = blockValue * .55; + } else if (mNodeCoins <= (nMoneySupply * .363) && mNodeCoins > (nMoneySupply * .35)) { + ret = blockValue * .54; + } else if (mNodeCoins <= (nMoneySupply * .376) && mNodeCoins > (nMoneySupply * .363)) { + ret = blockValue * .53; + } else if (mNodeCoins <= (nMoneySupply * .389) && mNodeCoins > (nMoneySupply * .376)) { + ret = blockValue * .52; + } else if (mNodeCoins <= (nMoneySupply * .402) && mNodeCoins > (nMoneySupply * .389)) { + ret = blockValue * .51; + } else if (mNodeCoins <= (nMoneySupply * .415) && mNodeCoins > (nMoneySupply * .402)) { + ret = blockValue * .50; + } else if (mNodeCoins <= (nMoneySupply * .428) && mNodeCoins > (nMoneySupply * .415)) { + ret = blockValue * .49; + } else if (mNodeCoins <= (nMoneySupply * .441) && mNodeCoins > (nMoneySupply * .428)) { + ret = blockValue * .48; + } else if (mNodeCoins <= (nMoneySupply * .454) && mNodeCoins > (nMoneySupply * .441)) { + ret = blockValue * .47; + } else if (mNodeCoins <= (nMoneySupply * .467) && mNodeCoins > (nMoneySupply * .454)) { + ret = blockValue * .46; + } else if (mNodeCoins <= (nMoneySupply * .48) && mNodeCoins > (nMoneySupply * .467)) { + ret = blockValue * .45; + } else if (mNodeCoins <= (nMoneySupply * .493) && mNodeCoins > (nMoneySupply * .48)) { + ret = blockValue * .44; + } else if (mNodeCoins <= (nMoneySupply * .506) && mNodeCoins > (nMoneySupply * .493)) { + ret = blockValue * .43; + } else if (mNodeCoins <= (nMoneySupply * .519) && mNodeCoins > (nMoneySupply * .506)) { + ret = blockValue * .42; + } else if (mNodeCoins <= (nMoneySupply * .532) && mNodeCoins > (nMoneySupply * .519)) { + ret = blockValue * .41; + } else if (mNodeCoins <= (nMoneySupply * .545) && mNodeCoins > (nMoneySupply * .532)) { + ret = blockValue * .40; + } else if (mNodeCoins <= (nMoneySupply * .558) && mNodeCoins > (nMoneySupply * .545)) { + ret = blockValue * .39; + } else if (mNodeCoins <= (nMoneySupply * .571) && mNodeCoins > (nMoneySupply * .558)) { + ret = blockValue * .38; + } else if (mNodeCoins <= (nMoneySupply * .584) && mNodeCoins > (nMoneySupply * .571)) { + ret = blockValue * .37; + } else if (mNodeCoins <= (nMoneySupply * .597) && mNodeCoins > (nMoneySupply * .584)) { + ret = blockValue * .36; + } else if (mNodeCoins <= (nMoneySupply * .61) && mNodeCoins > (nMoneySupply * .597)) { + ret = blockValue * .35; + } else if (mNodeCoins <= (nMoneySupply * .623) && mNodeCoins > (nMoneySupply * .61)) { + ret = blockValue * .34; + } else if (mNodeCoins <= (nMoneySupply * .636) && mNodeCoins > (nMoneySupply * .623)) { + ret = blockValue * .33; + } else if (mNodeCoins <= (nMoneySupply * .649) && mNodeCoins > (nMoneySupply * .636)) { + ret = blockValue * .32; + } else if (mNodeCoins <= (nMoneySupply * .662) && mNodeCoins > (nMoneySupply * .649)) { + ret = blockValue * .31; + } else if (mNodeCoins <= (nMoneySupply * .675) && mNodeCoins > (nMoneySupply * .662)) { + ret = blockValue * .30; + } else if (mNodeCoins <= (nMoneySupply * .688) && mNodeCoins > (nMoneySupply * .675)) { + ret = blockValue * .29; + } else if (mNodeCoins <= (nMoneySupply * .701) && mNodeCoins > (nMoneySupply * .688)) { + ret = blockValue * .28; + } else if (mNodeCoins <= (nMoneySupply * .714) && mNodeCoins > (nMoneySupply * .701)) { + ret = blockValue * .27; + } else if (mNodeCoins <= (nMoneySupply * .727) && mNodeCoins > (nMoneySupply * .714)) { + ret = blockValue * .26; + } else if (mNodeCoins <= (nMoneySupply * .74) && mNodeCoins > (nMoneySupply * .727)) { + ret = blockValue * .25; + } else if (mNodeCoins <= (nMoneySupply * .753) && mNodeCoins > (nMoneySupply * .74)) { + ret = blockValue * .24; + } else if (mNodeCoins <= (nMoneySupply * .766) && mNodeCoins > (nMoneySupply * .753)) { + ret = blockValue * .23; + } else if (mNodeCoins <= (nMoneySupply * .779) && mNodeCoins > (nMoneySupply * .766)) { + ret = blockValue * .22; + } else if (mNodeCoins <= (nMoneySupply * .792) && mNodeCoins > (nMoneySupply * .779)) { + ret = blockValue * .21; + } else if (mNodeCoins <= (nMoneySupply * .805) && mNodeCoins > (nMoneySupply * .792)) { + ret = blockValue * .20; + } else if (mNodeCoins <= (nMoneySupply * .818) && mNodeCoins > (nMoneySupply * .805)) { + ret = blockValue * .19; + } else if (mNodeCoins <= (nMoneySupply * .831) && mNodeCoins > (nMoneySupply * .818)) { + ret = blockValue * .18; + } else if (mNodeCoins <= (nMoneySupply * .844) && mNodeCoins > (nMoneySupply * .831)) { + ret = blockValue * .17; + } else if (mNodeCoins <= (nMoneySupply * .857) && mNodeCoins > (nMoneySupply * .844)) { + ret = blockValue * .16; + } else if (mNodeCoins <= (nMoneySupply * .87) && mNodeCoins > (nMoneySupply * .857)) { + ret = blockValue * .15; + } else if (mNodeCoins <= (nMoneySupply * .883) && mNodeCoins > (nMoneySupply * .87)) { + ret = blockValue * .14; + } else if (mNodeCoins <= (nMoneySupply * .896) && mNodeCoins > (nMoneySupply * .883)) { + ret = blockValue * .13; + } else if (mNodeCoins <= (nMoneySupply * .909) && mNodeCoins > (nMoneySupply * .896)) { + ret = blockValue * .12; + } else if (mNodeCoins <= (nMoneySupply * .922) && mNodeCoins > (nMoneySupply * .909)) { + ret = blockValue * .11; + } else if (mNodeCoins <= (nMoneySupply * .935) && mNodeCoins > (nMoneySupply * .922)) { + ret = blockValue * .10; + } else if (mNodeCoins <= (nMoneySupply * .945) && mNodeCoins > (nMoneySupply * .935)) { + ret = blockValue * .09; + } else if (mNodeCoins <= (nMoneySupply * .961) && mNodeCoins > (nMoneySupply * .945)) { + ret = blockValue * .08; + } else if (mNodeCoins <= (nMoneySupply * .974) && mNodeCoins > (nMoneySupply * .961)) { + ret = blockValue * .07; + } else if (mNodeCoins <= (nMoneySupply * .987) && mNodeCoins > (nMoneySupply * .974)) { + ret = blockValue * .06; + } else if (mNodeCoins <= (nMoneySupply * .99) && mNodeCoins > (nMoneySupply * .987)) { + ret = blockValue * .05; + } else { + ret = blockValue * .01; + } + } + return ret; } -*/ int64_t GetMasternodePayment(int nHeight, int64_t blockValue, int nMasternodeCount) { - int64_t ret = 0; - - // if (Params().NetworkID() == CBaseChainParams::TESTNET) { - // if (nHeight < 200) - // return 0; - // } - - // Changes alot and levels out to seesaw at end. - if (nHeight == 0) { - ret = blockValue * 0; - }else if (nHeight <= 25000 && nHeight > 200) { - ret = blockValue / 10 * 6; //60% - }else if (nHeight <= 60000 && nHeight > 25000) { - ret = blockValue / 10 * 6; //60% - }else if (nHeight <= 65000 && nHeight > 60000) { - ret = blockValue / 10 * 6.5; //65% - }else if (nHeight <= 70000 && nHeight > 65000) { - ret = blockValue / 10 * 6.6; //66% - }else if (nHeight <= 75000 && nHeight > 70000) { - ret = blockValue / 10 * 6.7; //67% - }else if (nHeight <= 80000 && nHeight > 75000) { - ret = blockValue / 10 * 6.8; //68% - }else if (nHeight <= 85000 && nHeight > 80000) { - ret = blockValue / 10 * 6.9; //69% - }else if (nHeight <= 88000 && nHeight > 85000) { - ret = blockValue / 10 * 7; //70% - }else if (nHeight <= 91000 && nHeight > 88000) { - ret = blockValue / 10 * 7.2; //72% - }else if (nHeight <= 94000 && nHeight > 91000) { - ret = blockValue / 10 * 7.4; //74% - }else if (nHeight <= 97000 && nHeight > 94000) { - ret = blockValue / 10 * 7.6; //76% - }else if (nHeight <= 100000 && nHeight > 97000) { - ret = blockValue / 10 * 7.8; //78% - }else if (nHeight <= 125000 && nHeight > 100000) { - ret = blockValue / 10 * 8; //80% - }else if (nHeight <= 150000 && nHeight > 125000) { - ret = blockValue / 10 * 8.5; //85% - }else if (nHeight <= 175000 && nHeight > 150000) { - ret = blockValue / 10 * 9; //90% - }else if (nHeight > 175000) { + int64_t ret = 0; + + // if (Params().NetworkID() == CBaseChainParams::TESTNET) { + // if (nHeight < 200) + // return 0; + // } + + // Changes alot and levels out to seesaw at end. + if (nHeight == 0) { + ret = blockValue * 0; + } else if (nHeight <= 25000 && nHeight > 200) { + ret = blockValue / 10 * 6; //60% + } else if (nHeight <= 60000 && nHeight > 25000) { + ret = blockValue / 10 * 6; //60% + } else if (nHeight <= 65000 && nHeight > 60000) { + ret = blockValue / 10 * 6.5; //65% + } else if (nHeight <= 70000 && nHeight > 65000) { + ret = blockValue / 10 * 6.6; //66% + } else if (nHeight <= 75000 && nHeight > 70000) { + ret = blockValue / 10 * 6.7; //67% + } else if (nHeight <= 80000 && nHeight > 75000) { + ret = blockValue / 10 * 6.8; //68% + } else if (nHeight <= 85000 && nHeight > 80000) { + ret = blockValue / 10 * 6.9; //69% + } else if (nHeight <= 88000 && nHeight > 85000) { + ret = blockValue / 10 * 7; //70% + } else if (nHeight <= 91000 && nHeight > 88000) { + ret = blockValue / 10 * 7.2; //72% + } else if (nHeight <= 94000 && nHeight > 91000) { + ret = blockValue / 10 * 7.4; //74% + } else if (nHeight <= 97000 && nHeight > 94000) { + ret = blockValue / 10 * 7.6; //76% + } else if (nHeight <= 100000 && nHeight > 97000) { + ret = blockValue / 10 * 7.8; //78% + } else if (nHeight <= 125000 && nHeight > 100000) { + ret = blockValue / 10 * 8; //80% + } else if (nHeight <= 150000 && nHeight > 125000) { + ret = blockValue / 10 * 8.5; //85% + } else if (nHeight <= 175000 && nHeight > 150000) { ret = blockValue / 10 * 9; //90% - } - return ret; + } else if (nHeight > 175000) { + ret = blockValue / 10 * 9; //90% + } + return ret; } //Treasury blocks start from 60,000 and then each block after int nStartTreasuryBlock = 60000; int nTreasuryBlockStep = 1440; //Checks to see if block count above is correct if not then no Treasury -//Put spork toggle to turn on and off. bool IsTreasuryBlock(int nHeight) { - //This is put in for when dev fee is turned off. - if (nHeight < nStartTreasuryBlock) - return false; - else if (IsSporkActive(SPORK_17_TREASURY_PAYMENT_ENFORCEMENT)) - return false; - else if ((nHeight - nStartTreasuryBlock) % nTreasuryBlockStep == 0) - return true; - else - return false; - - /* + //This is put in for when dev fee is turned off. + if (nHeight < nStartTreasuryBlock) + return false; + else if (IsSporkActive(SPORK_17_TREASURY_PAYMENT_ENFORCEMENT)) + return false; + else if ((nHeight - nStartTreasuryBlock) % nTreasuryBlockStep == 0) + return true; + else + return false; + + /* if (nHeight < nStartTreasuryBlock) return false; else if ((nHeight - nStartTreasuryBlock) % nTreasuryBlockStep == 0) @@ -2595,34 +2474,26 @@ bool IsTreasuryBlock(int nHeight) int64_t GetTreasuryAward(int nHeight) { - if (IsTreasuryBlock(nHeight)) { - if (nHeight < 75000 && nHeight > 60000) { - return 3600 * COIN; //3,600 aday at 5% 25 coins per block - } - else if (nHeight < 100000 && nHeight > 75000) { - return 6120 * COIN; //6,120 aday at 5% 42.5 coins per block - } - else if (nHeight < 125000 && nHeight > 100000) { - return 5400 * COIN; //5,400 aday at 5% 37.5 coins per block - } - else if (nHeight < 168000 && nHeight > 125000) { - return 3600 * COIN; //3,600 aday at 5% 25 coins per block - } - else if (nHeight < 297600 && nHeight > 168000) { - return 1800 * COIN; //1,800 aday at 5% 12.5 coins per block - } - else if (nHeight < 556800 && nHeight > 297600) { - return 720 * COIN; //720 aday at 5% 5 coins per block - } - else if (nHeight > 556800) { - return 360 * COIN; //720 aday at 5% 2.5 coins per block - } - else { - return 3600; - } - } - else - return 0; + if (IsTreasuryBlock(nHeight)) { + if (nHeight < 75000 && nHeight > 60000) { + return 3600 * COIN; //3,600 aday at 5% 25 coins per block + } else if (nHeight < 100000 && nHeight > 75000) { + return 6120 * COIN; //6,120 aday at 5% 42.5 coins per block + } else if (nHeight < 125000 && nHeight > 100000) { + return 5400 * COIN; //5,400 aday at 5% 37.5 coins per block + } else if (nHeight < 168000 && nHeight > 125000) { + return 3600 * COIN; //3,600 aday at 5% 25 coins per block + } else if (nHeight < 297600 && nHeight > 168000) { + return 1800 * COIN; //1,800 aday at 5% 12.5 coins per block + } else if (nHeight < 556800 && nHeight > 297600) { + return 720 * COIN; //720 aday at 5% 5 coins per block + } else if (nHeight < 556800) { + return 360 * COIN; //720 aday at 5% 2.5 coins per block + } else { + return 3600; + } + } else + return 0; } //Revive blocks start from 60,001 and then each block after @@ -2631,21 +2502,20 @@ int nReviveBlockStep = 1440; //Checks to see if block count above is correct if not then no Revive bool IsReviveBlock(int nHeight) { + // Old fee for AQX before admin gave up on project + // CCBC will not pay for revival fee since CCBC dev did all work + // And AQX team didnt help like promised. - // Old fee for AQX before admin gave up on project - // CCBC will not pay for revival fee since CCBC dev did all work - // And AQX team didnt help like promised. + if (nHeight < nStartReviveBlock) + return false; + else if (IsSporkActive(SPORK_18_REVIVE_PAYMENT_ENFORCEMENT)) + return false; + else if ((nHeight - nStartReviveBlock) % nReviveBlockStep == 0) + return true; + else + return false; - if (nHeight < nStartReviveBlock) - return false; - else if (IsSporkActive(SPORK_18_REVIVE_PAYMENT_ENFORCEMENT)) - return false; - else if ((nHeight - nStartReviveBlock) % nReviveBlockStep == 0) - return true; - else - return false; - - /* + /* if (nHeight < nStartReviveBlock) return false; else if ((nHeight - nStartReviveBlock) % nReviveBlockStep == 0) @@ -2657,50 +2527,42 @@ bool IsReviveBlock(int nHeight) int64_t GetReviveAward(int nHeight) { - if (IsReviveBlock(nHeight)) { - if (nHeight < 75000 && nHeight > 60000) { - return 3600 * COIN; //3,600 aday at 5% 25 coins per block - } - else if (nHeight < 100000 && nHeight > 75000) { - return 6120 * COIN; //6,120 aday at 5% 42.5 coins per block - } - else if (nHeight < 125000 && nHeight > 100000) { - return 5400 * COIN; //5,400 aday at 5% 37.5 coins per block - } - else if (nHeight < 168000 && nHeight > 125000) { - return 3600 * COIN; //3,600 aday at 5% 25 coins per block - } - else if (nHeight < 297600 && nHeight > 168000) { - return 1800 * COIN; //1,800 aday at 5% 12.5 coins per block - } - else if (nHeight < 556800 && nHeight > 297600) { - return 720 * COIN; //720 aday at 5% 5 coins per block - } - else if (nHeight > 556800) { - return 360 * COIN; //720 aday at 5% 2.5 coins per block - } - else { - return 3600; - } - } - else - return 0; + if (IsReviveBlock(nHeight)) { + if (nHeight < 75000 && nHeight > 60000) { + return 3600 * COIN; //3,600 aday at 5% 25 coins per block + } else if (nHeight < 100000 && nHeight > 75000) { + return 6120 * COIN; //6,120 aday at 5% 42.5 coins per block + } else if (nHeight < 125000 && nHeight > 100000) { + return 5400 * COIN; //5,400 aday at 5% 37.5 coins per block + } else if (nHeight < 168000 && nHeight > 125000) { + return 3600 * COIN; //3,600 aday at 5% 25 coins per block + } else if (nHeight < 297600 && nHeight > 168000) { + return 1800 * COIN; //1,800 aday at 5% 12.5 coins per block + } else if (nHeight < 556800 && nHeight > 297600) { + return 720 * COIN; //720 aday at 5% 5 coins per block + } else if (nHeight < 556800) { + return 360 * COIN; //720 aday at 5% 2.5 coins per block + } else { + return 3600; + } + } else + return 0; } bool IsInitialBlockDownload() { - LOCK(cs_main); - if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) - return true; - static bool lockIBDState = false; - if (lockIBDState) - return false; - bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - 6 * 60 * 60) && - chainActive.Height() > 3030; // ~144 blocks behind -> 2 x fork detection time - if (!state) - lockIBDState = true; - return state; + LOCK(cs_main); + if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) + return true; + static bool lockIBDState = false; + if (lockIBDState) + return false; + bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || + pindexBestHeader->GetBlockTime() < GetTime() - 6 * 60 * 60) && + chainActive.Height() > 3030; // ~144 blocks behind -> 2 x fork detection time + if (!state) + lockIBDState = true; + return state; } bool fLargeWorkForkFound = false; @@ -2709,405 +2571,399 @@ CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; void CheckForkWarningConditions() { - AssertLockHeld(cs_main); - // Before we get past initial download, we cannot reliably alert about forks - // (we assume we don't get stuck on a fork before the last checkpoint) - if (IsInitialBlockDownload()) - return; - - // If our best fork is no longer within 72 blocks (+/- 3 hours if no one mines it) - // of our head, drop it - if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) - pindexBestForkTip = NULL; - - if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { - if (!fLargeWorkForkFound && pindexBestForkBase) { - if (pindexBestForkBase->phashBlock) { - std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); - CAlert::Notify(warning, true); - } - } - if (pindexBestForkTip && pindexBestForkBase) { - if (pindexBestForkBase->phashBlock) { - LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); - fLargeWorkForkFound = true; - } - } - else { - LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); - fLargeWorkInvalidChainFound = true; - } - } - else { - fLargeWorkForkFound = false; - fLargeWorkInvalidChainFound = false; - } + AssertLockHeld(cs_main); + // Before we get past initial download, we cannot reliably alert about forks + // (we assume we don't get stuck on a fork before the last checkpoint) + if (IsInitialBlockDownload()) + return; + + // If our best fork is no longer within 72 blocks (+/- 3 hours if no one mines it) + // of our head, drop it + if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) + pindexBestForkTip = NULL; + + if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { + if (!fLargeWorkForkFound && pindexBestForkBase) { + if (pindexBestForkBase->phashBlock) { + std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + + pindexBestForkBase->phashBlock->ToString() + std::string("'"); + CAlert::Notify(warning, true); + } + } + if (pindexBestForkTip && pindexBestForkBase) { + if (pindexBestForkBase->phashBlock) { + LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); + fLargeWorkForkFound = true; + } + } else { + LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); + fLargeWorkInvalidChainFound = true; + } + } else { + fLargeWorkForkFound = false; + fLargeWorkInvalidChainFound = false; + } } void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) { - AssertLockHeld(cs_main); - // If we are on a fork that is sufficiently large, set a warning flag - CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = chainActive.Tip(); - while (pfork && pfork != plonger) { - while (plonger && plonger->nHeight > pfork->nHeight) - plonger = plonger->pprev; - if (pfork == plonger) - break; - pfork = pfork->pprev; - } - - // We define a condition which we should warn the user about as a fork of at least 7 blocks - // who's tip is within 72 blocks (+/- 3 hours if no one mines it) of ours - // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) - // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network - // hash rate operating on the fork. - // We define it this way because it allows us to only store the highest fork tip (+ base) which meets - // the 7-block condition and from this always have the most-likely-to-cause-warning fork - if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && - pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && - chainActive.Height() - pindexNewForkTip->nHeight < 72) { - pindexBestForkTip = pindexNewForkTip; - pindexBestForkBase = pfork; - } - - CheckForkWarningConditions(); + AssertLockHeld(cs_main); + // If we are on a fork that is sufficiently large, set a warning flag + CBlockIndex* pfork = pindexNewForkTip; + CBlockIndex* plonger = chainActive.Tip(); + while (pfork && pfork != plonger) { + while (plonger && plonger->nHeight > pfork->nHeight) + plonger = plonger->pprev; + if (pfork == plonger) + break; + pfork = pfork->pprev; + } + + // We define a condition which we should warn the user about as a fork of at least 7 blocks + // who's tip is within 72 blocks (+/- 3 hours if no one mines it) of ours + // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) + // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network + // hash rate operating on the fork. + // We define it this way because it allows us to only store the highest fork tip (+ base) which meets + // the 7-block condition and from this always have the most-likely-to-cause-warning fork + if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && + pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && + chainActive.Height() - pindexNewForkTip->nHeight < 72) { + pindexBestForkTip = pindexNewForkTip; + pindexBestForkBase = pfork; + } + + CheckForkWarningConditions(); } // Requires cs_main. void Misbehaving(NodeId pnode, int howmuch) { - if (howmuch == 0) - return; + if (howmuch == 0) + return; - CNodeState* state = State(pnode); - if (state == NULL) - return; + CNodeState* state = State(pnode); + if (state == NULL) + return; - state->nMisbehavior += howmuch; - int banscore = GetArg("-banscore", 100); - if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { - LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior - howmuch, state->nMisbehavior); - state->fShouldBan = true; - } - else - LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior - howmuch, state->nMisbehavior); + state->nMisbehavior += howmuch; + int banscore = GetArg("-banscore", 100); + if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { + LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior - howmuch, state->nMisbehavior); + state->fShouldBan = true; + } else + LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior - howmuch, state->nMisbehavior); } void static InvalidChainFound(CBlockIndex* pindexNew) { - if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) - pindexBestInvalid = pindexNew; + if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) + pindexBestInvalid = pindexNew; - LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", - pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble()) / log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime())); - LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); - CheckForkWarningConditions(); + LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", + pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble()) / log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime())); + LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); + CheckForkWarningConditions(); } void static InvalidBlockFound(CBlockIndex* pindex, const CValidationState& state) { - int nDoS = 0; - if (state.IsInvalid(nDoS)) { - std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); - if (it != mapBlockSource.end() && State(it->second)) { - CBlockReject reject = { state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash() }; - State(it->second)->rejects.push_back(reject); - if (nDoS > 0) - Misbehaving(it->second, nDoS); - } - } - if (!state.CorruptionPossible()) { - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - setBlockIndexCandidates.erase(pindex); - InvalidChainFound(pindex); - } + int nDoS = 0; + if (state.IsInvalid(nDoS)) { + std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); + if (it != mapBlockSource.end() && State(it->second)) { + CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; + State(it->second)->rejects.push_back(reject); + if (nDoS > 0) + Misbehaving(it->second, nDoS); + } + } + if (!state.CorruptionPossible()) { + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + setBlockIndexCandidates.erase(pindex); + InvalidChainFound(pindex); + } } void UpdateCoins(const CTransaction& tx, CValidationState& state, CCoinsViewCache& inputs, CTxUndo& txundo, int nHeight) { - // mark inputs spent - if (!tx.IsCoinBase() && !tx.IsZerocoinSpend()) { - txundo.vprevout.reserve(tx.vin.size()); - BOOST_FOREACH(const CTxIn& txin, tx.vin) { - txundo.vprevout.push_back(CTxInUndo()); - bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout, txundo.vprevout.back()); - assert(ret); - } - } + // mark inputs spent + if (!tx.IsCoinBase() && !tx.IsZerocoinSpend()) { + txundo.vprevout.reserve(tx.vin.size()); + BOOST_FOREACH (const CTxIn& txin, tx.vin) { + txundo.vprevout.push_back(CTxInUndo()); + bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout, txundo.vprevout.back()); + assert(ret); + } + } - // add outputs - inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); + // add outputs + inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); } bool CScriptCheck::operator()() { - const CScript& scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { - return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); - } - return true; + const CScript& scriptSig = ptxTo->vin[nIn].scriptSig; + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); + } + return true; } bool CheckInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector* pvChecks) { - if (!tx.IsCoinBase() && !tx.IsZerocoinSpend()) { - if (pvChecks) - pvChecks->reserve(tx.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 (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString())); - - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; - int nSpendHeight = pindexPrev->nHeight + 1; - CAmount nValueIn = 0; - CAmount nFees = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint& prevout = tx.vin[i].prevout; - const CCoins* coins = inputs.AccessCoins(prevout.hash); - assert(coins); - - // If prev is coinbase, check that it's matured - if (coins->IsCoinBase() || coins->IsCoinStake()) { - if (nSpendHeight - coins->nHeight < Params().COINBASE_MATURITY()) - return state.Invalid( - error("CheckInputs() : tried to spend coinbase at depth %d, coinstake=%d", nSpendHeight - coins->nHeight, coins->IsCoinStake()), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); - } - - // 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"), - REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - } - - if (!tx.IsCoinStake()) { - if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in (%s) < value out (%s)", tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), - REJECT_INVALID, "bad-txns-in-belowout"); - - // Tally transaction fees - CAmount nTxFee = nValueIn - tx.GetValueOut(); - if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString()), - REJECT_INVALID, "bad-txns-fee-negative"); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs() : nFees out of range"), - REJECT_INVALID, "bad-txns-fee-outofrange"); - } - // 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 < tx.vin.size(); i++) { - const COutPoint& prevout = tx.vin[i].prevout; - const CCoins* coins = inputs.AccessCoins(prevout.hash); - assert(coins); - - // Verify signature - CScriptCheck check(*coins, tx, i, flags, cacheStore); - if (pvChecks) { - pvChecks->push_back(CScriptCheck()); - check.swap(pvChecks->back()); - } - else if (!check()) { - if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { - // Check whether the failure was caused by a - // non-mandatory script verification check, such as - // non-standard DER encodings or non-null dummy - // arguments; if so, don't trigger DoS protection to - // avoid splitting the network between upgraded and - // non-upgraded nodes. - CScriptCheck check(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); - if (check()) - return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); - } - // Failures of other flags indicate a transaction that is - // invalid in new blocks, e.g. a invalid P2SH. We DoS ban - // such nodes as they are not following the protocol. That - // said during an upgrade careful thought should be taken - // as to the correct behavior - we may want to continue - // peering with non-upgraded nodes even after a soft-fork - // super-majority vote has passed. - return state.DoS(100, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); - } - } - } - } + if (!tx.IsCoinBase() && !tx.IsZerocoinSpend()) { + if (pvChecks) + pvChecks->reserve(tx.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 (!inputs.HaveInputs(tx)) + return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString())); + + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; + CAmount nValueIn = 0; + CAmount nFees = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint& prevout = tx.vin[i].prevout; + const CCoins* coins = inputs.AccessCoins(prevout.hash); + assert(coins); + + // If prev is coinbase, check that it's matured + if (coins->IsCoinBase() || coins->IsCoinStake()) { + if (nSpendHeight - coins->nHeight < Params().COINBASE_MATURITY()) + return state.Invalid( + error("CheckInputs() : tried to spend coinbase at depth %d, coinstake=%d", nSpendHeight - coins->nHeight, coins->IsCoinStake()), + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + } - return true; + // 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"), + REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + } + + if (!tx.IsCoinStake()) { + if (nValueIn < tx.GetValueOut()) + return state.DoS(100, error("CheckInputs() : %s value in (%s) < value out (%s)", tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), + REJECT_INVALID, "bad-txns-in-belowout"); + + // Tally transaction fees + CAmount nTxFee = nValueIn - tx.GetValueOut(); + if (nTxFee < 0) + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString()), + REJECT_INVALID, "bad-txns-fee-negative"); + nFees += nTxFee; + if (!MoneyRange(nFees)) + return state.DoS(100, error("CheckInputs() : nFees out of range"), + REJECT_INVALID, "bad-txns-fee-outofrange"); + } + // 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 < tx.vin.size(); i++) { + const COutPoint& prevout = tx.vin[i].prevout; + const CCoins* coins = inputs.AccessCoins(prevout.hash); + assert(coins); + + // Verify signature + CScriptCheck check(*coins, tx, i, flags, cacheStore); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) { + if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { + // Check whether the failure was caused by a + // non-mandatory script verification check, such as + // non-standard DER encodings or non-null dummy + // arguments; if so, don't trigger DoS protection to + // avoid splitting the network between upgraded and + // non-upgraded nodes. + CScriptCheck check(*coins, tx, i, + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); + if (check()) + return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); + } + // Failures of other flags indicate a transaction that is + // invalid in new blocks, e.g. a invalid P2SH. We DoS ban + // such nodes as they are not following the protocol. That + // said during an upgrade careful thought should be taken + // as to the correct behavior - we may want to continue + // peering with non-upgraded nodes even after a soft-fork + // super-majority vote has passed. + return state.DoS(100, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); + } + } + } + } + + return true; } bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { - assert(pindex->GetBlockHash() == 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 != block.vtx.size()) - return error("DisconnectBlock() : block and undo data inconsistent"); - - // undo transactions in reverse order - for (int i = block.vtx.size() - 1; i >= 0; i--) { - const CTransaction& tx = block.vtx[i]; - - /** UNDO ZEROCOIN DATABASING - * note we only undo zerocoin databasing in the following statement, value to and from Ccbc - * addresses should still be handled by the typical bitcoin based undo code - * */ - if (tx.ContainsZerocoins()) { - if (tx.IsZerocoinSpend()) { - //erase all zerocoinspends in this transaction - for (const CTxIn txin : tx.vin) { - if (txin.scriptSig.IsZerocoinSpend()) { - CoinSpend spend = TxInToZerocoinSpend(txin); - if (!zerocoinDB->EraseCoinSpend(spend.getCoinSerialNumber())) - return error("failed to erase spent zerocoin in block"); - } - } - } - if (tx.IsZerocoinMint()) { - //erase all zerocoinmints in this transaction - for (const CTxOut txout : tx.vout) { - if (txout.scriptPubKey.empty() || !txout.scriptPubKey.IsZerocoinMint()) - continue; - - PublicCoin pubCoin(Params().Zerocoin_Params()); - if (!TxOutToPublicCoin(txout, pubCoin, state)) - return error("DisconnectBlock(): TxOutToPublicCoin() failed"); - - if (!zerocoinDB->EraseCoinMint(pubCoin.getValue())) - return error("DisconnectBlock(): Failed to erase coin mint"); - } - } - } - - uint256 hash = tx.GetHash(); - - // Check that all outputs are available and match the outputs in the block itself - // exactly. Note that transactions with only provably unspendable outputs won't - // have outputs available even in the block itself, so we handle that case - // specially with outsEmpty. - { - CCoins outsEmpty; - CCoinsModifier outs = view.ModifyCoins(hash); - outs->ClearUnspendable(); - - CCoins outsBlock(tx, pindex->nHeight); - // The CCoins serialization does not serialize negative numbers. - // No network rules currently depend on the version here, so an inconsistency is harmless - // but it must be corrected before txout nversion ever influences a network rule. - if (outsBlock.nVersion < 0) - outs->nVersion = outsBlock.nVersion; - if (*outs != outsBlock) - fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); - - // remove outputs - outs->Clear(); - } - - // restore inputs - if (!tx.IsCoinBase() && !tx.IsZerocoinSpend()) { // not coinbases or zerocoinspend because they dont have traditional inputs - const CTxUndo& txundo = blockUndo.vtxundo[i - 1]; - if (txundo.vprevout.size() != tx.vin.size()) - return error("DisconnectBlock() : transaction and undo data inconsistent - txundo.vprevout.siz=%d tx.vin.siz=%d", txundo.vprevout.size(), tx.vin.size()); - for (unsigned int j = tx.vin.size(); j-- > 0;) { - const COutPoint& out = tx.vin[j].prevout; - const CTxInUndo& undo = txundo.vprevout[j]; - CCoinsModifier coins = view.ModifyCoins(out.hash); - 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->Clear(); - 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; - } - } - } - - // move best block pointer to prevout block - view.SetBestBlock(pindex->pprev->GetBlockHash()); - - if (!fVerifyingBlocks) { - //if block is an accumulator checkpoint block, remove checkpoint and checksums from db - uint256 nCheckpoint = pindex->nAccumulatorCheckpoint; - if (nCheckpoint != pindex->pprev->nAccumulatorCheckpoint) { - if (!EraseAccumulatorValues(nCheckpoint, pindex->pprev->nAccumulatorCheckpoint)) - return error("DisconnectBlock(): failed to erase checkpoint"); - } - } - - if (pfClean) { - *pfClean = fClean; - return true; - } - else { - return fClean; - } + assert(pindex->GetBlockHash() == 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 != block.vtx.size()) + return error("DisconnectBlock() : block and undo data inconsistent"); + + // undo transactions in reverse order + for (int i = block.vtx.size() - 1; i >= 0; i--) { + const CTransaction& tx = block.vtx[i]; + + /** UNDO ZEROCOIN DATABASING + * note we only undo zerocoin databasing in the following statement, value to and from Ccbc + * addresses should still be handled by the typical bitcoin based undo code + * */ + if (tx.ContainsZerocoins()) { + if (tx.IsZerocoinSpend()) { + //erase all zerocoinspends in this transaction + for (const CTxIn txin : tx.vin) { + if (txin.scriptSig.IsZerocoinSpend()) { + CoinSpend spend = TxInToZerocoinSpend(txin); + if (!zerocoinDB->EraseCoinSpend(spend.getCoinSerialNumber())) + return error("failed to erase spent zerocoin in block"); + } + } + } + if (tx.IsZerocoinMint()) { + //erase all zerocoinmints in this transaction + for (const CTxOut txout : tx.vout) { + if (txout.scriptPubKey.empty() || !txout.scriptPubKey.IsZerocoinMint()) + continue; + + PublicCoin pubCoin(Params().Zerocoin_Params()); + if (!TxOutToPublicCoin(txout, pubCoin, state)) + return error("DisconnectBlock(): TxOutToPublicCoin() failed"); + + if (!zerocoinDB->EraseCoinMint(pubCoin.getValue())) + return error("DisconnectBlock(): Failed to erase coin mint"); + } + } + } + + uint256 hash = tx.GetHash(); + + // Check that all outputs are available and match the outputs in the block itself + // exactly. Note that transactions with only provably unspendable outputs won't + // have outputs available even in the block itself, so we handle that case + // specially with outsEmpty. + { + CCoins outsEmpty; + CCoinsModifier outs = view.ModifyCoins(hash); + outs->ClearUnspendable(); + + CCoins outsBlock(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs->nVersion = outsBlock.nVersion; + if (*outs != outsBlock) + fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); + + // remove outputs + outs->Clear(); + } + + // restore inputs + if (!tx.IsCoinBase() && !tx.IsZerocoinSpend()) { // not coinbases or zerocoinspend because they dont have traditional inputs + const CTxUndo& txundo = blockUndo.vtxundo[i - 1]; + if (txundo.vprevout.size() != tx.vin.size()) + return error("DisconnectBlock() : transaction and undo data inconsistent - txundo.vprevout.siz=%d tx.vin.siz=%d", txundo.vprevout.size(), tx.vin.size()); + for (unsigned int j = tx.vin.size(); j-- > 0;) { + const COutPoint& out = tx.vin[j].prevout; + const CTxInUndo& undo = txundo.vprevout[j]; + CCoinsModifier coins = view.ModifyCoins(out.hash); + 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->Clear(); + 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; + } + } + } + + // move best block pointer to prevout block + view.SetBestBlock(pindex->pprev->GetBlockHash()); + + if (!fVerifyingBlocks) { + //if block is an accumulator checkpoint block, remove checkpoint and checksums from db + uint256 nCheckpoint = pindex->nAccumulatorCheckpoint; + if (nCheckpoint != pindex->pprev->nAccumulatorCheckpoint) { + if (!EraseAccumulatorValues(nCheckpoint, pindex->pprev->nAccumulatorCheckpoint)) + return error("DisconnectBlock(): failed to erase checkpoint"); + } + } + + if (pfClean) { + *pfClean = fClean; + return true; + } else { + return fClean; + } } void static FlushBlockFile(bool fFinalize = false) { - LOCK(cs_LastBlockFile); + LOCK(cs_LastBlockFile); - CDiskBlockPos posOld(nLastBlockFile, 0); + CDiskBlockPos posOld(nLastBlockFile, 0); - FILE* fileOld = OpenBlockFile(posOld); - if (fileOld) { - if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); - FileCommit(fileOld); - fclose(fileOld); - } + FILE* fileOld = OpenBlockFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); + FileCommit(fileOld); + fclose(fileOld); + } - fileOld = OpenUndoFile(posOld); - if (fileOld) { - if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); - FileCommit(fileOld); - fclose(fileOld); - } + fileOld = OpenUndoFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); + FileCommit(fileOld); + fclose(fileOld); + } } bool FindUndoPos(CValidationState& state, int nFile, CDiskBlockPos& pos, unsigned int nAddSize); @@ -3116,133 +2972,133 @@ static CCheckQueue scriptcheckqueue(128); void ThreadScriptCheck() { - RenameThread("ccbc-scriptch"); - scriptcheckqueue.Thread(); + RenameThread("ccbc-scriptch"); + scriptcheckqueue.Thread(); } void RecalculateZCCBCMinted() { - CBlockIndex* pindex = chainActive[Params().Zerocoin_AccumulatorStartHeight()]; - int nHeightEnd = chainActive.Height(); - while (true) { - if (pindex->nHeight % 1000 == 0) - LogPrintf("%s : block %d...\n", __func__, pindex->nHeight); + CBlockIndex* pindex = chainActive[Params().Zerocoin_AccumulatorStartHeight()]; + int nHeightEnd = chainActive.Height(); + while (true) { + if (pindex->nHeight % 1000 == 0) + LogPrintf("%s : block %d...\n", __func__, pindex->nHeight); - //overwrite possibly wrong vMintsInBlock data - CBlock block; - assert(ReadBlockFromDisk(block, pindex)); + //overwrite possibly wrong vMintsInBlock data + CBlock block; + assert(ReadBlockFromDisk(block, pindex)); - std::list listMints; - BlockToZerocoinMintList(block, listMints); + std::list listMints; + BlockToZerocoinMintList(block, listMints); - vector vDenomsBefore = pindex->vMintDenominationsInBlock; - pindex->vMintDenominationsInBlock.clear(); - for (auto mint : listMints) - pindex->vMintDenominationsInBlock.emplace_back(mint.GetDenomination()); + vector vDenomsBefore = pindex->vMintDenominationsInBlock; + pindex->vMintDenominationsInBlock.clear(); + for (auto mint : listMints) + pindex->vMintDenominationsInBlock.emplace_back(mint.GetDenomination()); - //Record mints to disk - assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))); + //Record mints to disk + assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))); - if (pindex->nHeight < nHeightEnd) - pindex = chainActive.Next(pindex); - else - break; - } + if (pindex->nHeight < nHeightEnd) + pindex = chainActive.Next(pindex); + else + break; + } - pblocktree->Flush(); + pblocktree->Flush(); } void RecalculateZCCBCSpent() { - CBlockIndex* pindex = chainActive[Params().Zerocoin_AccumulatorStartHeight()]; - while (true) { - if (pindex->nHeight % 1000 == 0) - LogPrintf("%s : block %d...\n", __func__, pindex->nHeight); + CBlockIndex* pindex = chainActive[Params().Zerocoin_AccumulatorStartHeight()]; + while (true) { + if (pindex->nHeight % 1000 == 0) + LogPrintf("%s : block %d...\n", __func__, pindex->nHeight); - //Rewrite zCCBC supply - CBlock block; - assert(ReadBlockFromDisk(block, pindex)); + //Rewrite zCCBC supply + CBlock block; + assert(ReadBlockFromDisk(block, pindex)); - list listDenomsSpent = ZerocoinSpendListFromBlock(block); + list listDenomsSpent = ZerocoinSpendListFromBlock(block); - //Reset the supply to previous block - pindex->mapZerocoinSupply = pindex->pprev->mapZerocoinSupply; + //Reset the supply to previous block + pindex->mapZerocoinSupply = pindex->pprev->mapZerocoinSupply; - //Add mints to zCCBC supply - for (auto denom : libzerocoin::zerocoinDenomList) { - long nDenomAdded = count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), denom); - pindex->mapZerocoinSupply.at(denom) += nDenomAdded; - } + //Add mints to zCCBC supply + for (auto denom : libzerocoin::zerocoinDenomList) { + long nDenomAdded = count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), denom); + pindex->mapZerocoinSupply.at(denom) += nDenomAdded; + } - //Remove spends from zCCBC supply - for (auto denom : listDenomsSpent) - pindex->mapZerocoinSupply.at(denom)--; + //Remove spends from zCCBC supply + for (auto denom : listDenomsSpent) + pindex->mapZerocoinSupply.at(denom)--; - //Rewrite money supply - assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))); + //Rewrite money supply + assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))); - if (pindex->nHeight < chainActive.Height()) - pindex = chainActive.Next(pindex); - else - break; - } - pblocktree->Flush(); + if (pindex->nHeight < chainActive.Height()) + pindex = chainActive.Next(pindex); + else + break; + } + pblocktree->Flush(); } bool RecalculateCCBCSupply(int nHeightStart) { - if (nHeightStart > chainActive.Height()) - return false; - - CBlockIndex* pindex = chainActive[nHeightStart]; - CAmount nSupplyPrev = pindex->pprev->nMoneySupply; - - while (true) { - if (pindex->nHeight % 1000 == 0) - LogPrintf("%s : block %d...\n", __func__, pindex->nHeight); - - CBlock block; - assert(ReadBlockFromDisk(block, pindex)); - - CAmount nValueIn = 0; - CAmount nValueOut = 0; - for (const CTransaction tx : block.vtx) { - for (unsigned int i = 0; i < tx.vin.size(); i++) { - if (tx.IsCoinBase()) - break; - - if (tx.vin[i].scriptSig.IsZerocoinSpend()) { - nValueIn += tx.vin[i].nSequence * COIN; - continue; - } - - COutPoint prevout = tx.vin[i].prevout; - CTransaction txPrev; - uint256 hashBlock; - assert(GetTransaction(prevout.hash, txPrev, hashBlock, true)); - nValueIn += txPrev.vout[prevout.n].nValue; - } - - for (unsigned int i = 0; i < tx.vout.size(); i++) { - if (i == 0 && tx.IsCoinStake()) - continue; - - nValueOut += tx.vout[i].nValue; - } - } - - // Rewrite money supply - pindex->nMoneySupply = nSupplyPrev + nValueOut - nValueIn; - nSupplyPrev = pindex->nMoneySupply; - assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))); - - if (pindex->nHeight < chainActive.Height()) - pindex = chainActive.Next(pindex); - else - break; - } - pblocktree->Flush(); - return true; + if (nHeightStart > chainActive.Height()) + return false; + + CBlockIndex* pindex = chainActive[nHeightStart]; + CAmount nSupplyPrev = pindex->pprev->nMoneySupply; + + while (true) { + if (pindex->nHeight % 1000 == 0) + LogPrintf("%s : block %d...\n", __func__, pindex->nHeight); + + CBlock block; + assert(ReadBlockFromDisk(block, pindex)); + + CAmount nValueIn = 0; + CAmount nValueOut = 0; + for (const CTransaction tx : block.vtx) { + for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsCoinBase()) + break; + + if (tx.vin[i].scriptSig.IsZerocoinSpend()) { + nValueIn += tx.vin[i].nSequence * COIN; + continue; + } + + COutPoint prevout = tx.vin[i].prevout; + CTransaction txPrev; + uint256 hashBlock; + assert(GetTransaction(prevout.hash, txPrev, hashBlock, true)); + nValueIn += txPrev.vout[prevout.n].nValue; + } + + for (unsigned int i = 0; i < tx.vout.size(); i++) { + if (i == 0 && tx.IsCoinStake()) + continue; + + nValueOut += tx.vout[i].nValue; + } + } + + // Rewrite money supply + pindex->nMoneySupply = nSupplyPrev + nValueOut - nValueIn; + nSupplyPrev = pindex->nMoneySupply; + assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))); + + if (pindex->nHeight < chainActive.Height()) + pindex = chainActive.Next(pindex); + else + break; + } + pblocktree->Flush(); + return true; } static int64_t nTimeVerify = 0; @@ -3253,462 +3109,459 @@ static int64_t nTimeTotal = 0; bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck, bool fAlreadyChecked) { - AssertLockHeld(cs_main); - // Check it again in case a previous version let a bad block in - if (!fAlreadyChecked && !CheckBlock(block, state, !fJustCheck, !fJustCheck)) - return false; - - // verify that the view's current state corresponds to the previous block - uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash(); - if (hashPrevBlock != view.GetBestBlock()) - LogPrintf("%s: hashPrev=%s view=%s\n", __func__, hashPrevBlock.ToString().c_str(), view.GetBestBlock().ToString().c_str()); - assert(hashPrevBlock == view.GetBestBlock()); - - // Special case for the genesis block, skipping connection of its transactions - // (its coinbase is unspendable) - if (block.GetHash() == Params().HashGenesisBlock()) { - view.SetBestBlock(pindex->GetBlockHash()); - return true; - } - - if (pindex->nHeight <= Params().LAST_POW_BLOCK() && block.IsProofOfStake()) - return state.DoS(100, error("ConnectBlock() : PoS period not active"), - REJECT_INVALID, "PoS-early"); - - if (pindex->nHeight > Params().LAST_POW_BLOCK() && block.IsProofOfWork()) - return state.DoS(100, error("ConnectBlock() : PoW period ended"), - REJECT_INVALID, "PoW-ended"); - - 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) { - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - const CCoins* coins = view.AccessCoins(tx.GetHash()); - if (coins && !coins->IsPruned()) - return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), - REJECT_INVALID, "bad-txns-BIP30"); - } - } - - // BIP16 didn't become active until Apr 1 2012 - int64_t nBIP16SwitchTime = 1333238400; - bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); - - unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; - - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: - if (block.nVersion >= 3 && CBlockIndex::IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { - flags |= SCRIPT_VERIFY_DERSIG; - } - - CBlockUndo blockundo; - - CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - - int64_t nTimeStart = GetTimeMicros(); - CAmount nFees = 0; - int nInputs = 0; - unsigned int nSigOps = 0; - CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); - std::vector > vPos; - vPos.reserve(block.vtx.size()); - blockundo.vtxundo.reserve(block.vtx.size() - 1); - CAmount nValueOut = 0; - CAmount nValueIn = 0; - unsigned int nMaxBlockSigOps = MAX_BLOCK_SIGOPS_CURRENT; - for (unsigned int i = 0; i < block.vtx.size(); i++) { - const CTransaction& tx = block.vtx[i]; - - nInputs += tx.vin.size(); - nSigOps += GetLegacySigOpCount(tx); - if (nSigOps > nMaxBlockSigOps) - return state.DoS(100, error("ConnectBlock() : too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - - //Temporarily disable zerocoin transactions for maintenance - if (block.nTime > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && !IsInitialBlockDownload() && tx.ContainsZerocoins()) - return state.DoS(100, error("ConnectBlock() : zerocoin transactions are currently in maintenance mode")); - - if (tx.IsZerocoinSpend()) { - int nHeightTx = 0; - if (IsTransactionInChain(tx.GetHash(), nHeightTx)) { - //when verifying blocks on init, the blocks are scanned without being disconnected - prevent that from causing an error - if (!fVerifyingBlocks || (fVerifyingBlocks && pindex->nHeight > nHeightTx)) - return state.DoS(100, error("%s : txid %s already exists in block %d , trying to include it again in block %d", __func__, tx.GetHash().GetHex(), nHeightTx, pindex->nHeight), - REJECT_INVALID, "bad-txns-inputs-missingorspent"); - } - - //Check for double spending of serial #'s - for (const CTxIn& txIn : tx.vin) { - if (!txIn.scriptSig.IsZerocoinSpend()) - continue; - CoinSpend spend = TxInToZerocoinSpend(txIn); - nValueIn += spend.getDenomination() * COIN; - - // Make sure that the serial number is in valid range - if (!spend.HasValidSerial(Params().Zerocoin_Params())) { - string strError = strprintf("%s : txid=%s in block %d contains invalid serial %s\n", __func__, tx.GetHash().GetHex(), pindex->nHeight, spend.getCoinSerialNumber()); - if (pindex->nHeight >= Params().Zerocoin_Block_EnforceSerialRange()) - return state.DoS(100, error(strError.c_str())); - strError = "NOT ENFORCING : " + strError; - LogPrintf(strError.c_str()); - } - - //Is the serial already in the blockchain? - uint256 hashTxFromDB; - int nHeightTxSpend = 0; - if (zerocoinDB->ReadCoinSpend(spend.getCoinSerialNumber(), hashTxFromDB)) { - if (IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTxSpend)) { - if (!fVerifyingBlocks || (fVerifyingBlocks && pindex->nHeight > nHeightTxSpend)) - return state.DoS(100, error("%s : zCcbc with serial %s is already in the block %d\n", - __func__, spend.getCoinSerialNumber().GetHex(), nHeightTxSpend)); - } - } - - //record spend to database - if (!zerocoinDB->WriteCoinSpend(spend.getCoinSerialNumber(), tx.GetHash())) - return error("%s : failed to record coin serial to database"); - } - } - else if (!tx.IsCoinBase()) { - if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock() : inputs missing/spent"), - REJECT_INVALID, "bad-txns-inputs-missingorspent"); - - 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 += GetP2SHSigOpCount(tx, view); - if (nSigOps > nMaxBlockSigOps) - return state.DoS(100, error("ConnectBlock() : too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - } - - if (!tx.IsCoinStake()) - nFees += view.GetValueIn(tx) - tx.GetValueOut(); - nValueIn += view.GetValueIn(tx); - - std::vector vChecks; - if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) - return false; - control.Add(vChecks); - } - nValueOut += tx.GetValueOut(); - - CTxUndo undoDummy; - if (i > 0) { - blockundo.vtxundo.push_back(CTxUndo()); - } - UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); - - vPos.push_back(std::make_pair(tx.GetHash(), pos)); - pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); - } - - std::list listMints; - BlockToZerocoinMintList(block, listMints); - std::list listSpends = ZerocoinSpendListFromBlock(block); - - if (!fVerifyingBlocks && pindex->nHeight == Params().Zerocoin_StartHeight() + 1) { - RecalculateZCCBCMinted(); - RecalculateZCCBCSpent(); - RecalculateCCBCSupply(1); - } - - // Initialize zerocoin supply to the supply from previous block - if (pindex->pprev && pindex->pprev->GetBlockHeader().nVersion > 3) { - for (auto& denom : zerocoinDenomList) { - pindex->mapZerocoinSupply.at(denom) = pindex->pprev->mapZerocoinSupply.at(denom); - } - } - - // Track zerocoin money supply - CAmount nAmountZerocoinSpent = 0; - pindex->vMintDenominationsInBlock.clear(); - if (pindex->pprev) { - for (auto& m : listMints) { - libzerocoin::CoinDenomination denom = m.GetDenomination(); - pindex->vMintDenominationsInBlock.push_back(m.GetDenomination()); - pindex->mapZerocoinSupply.at(denom)++; - } - - for (auto& denom : listSpends) { - pindex->mapZerocoinSupply.at(denom)--; - nAmountZerocoinSpent += libzerocoin::ZerocoinDenominationToAmount(denom); - - // zerocoin failsafe - if (pindex->mapZerocoinSupply.at(denom) < 0) - return state.DoS(100, error("Block contains zerocoins that spend more than are in the available supply to spend")); - } - } - - for (auto& denom : zerocoinDenomList) { - LogPrint("zero" - "%s coins for denomination %d pubcoin %s\n", - __func__, pindex->mapZerocoinSupply.at(denom), denom); - } - - // track money supply and mint amount info - CAmount nMoneySupplyPrev = pindex->pprev ? pindex->pprev->nMoneySupply : 0; - pindex->nMoneySupply = nMoneySupplyPrev + nValueOut - nValueIn; - pindex->nMint = pindex->nMoneySupply - nMoneySupplyPrev + nFees; - - // LogPrintf("XX69----------> ConnectBlock(): nValueOut: %s, nValueIn: %s, nFees: %s, nMint: %s zCcbcSpent: %s\n", - // FormatMoney(nValueOut), FormatMoney(nValueIn), - // FormatMoney(nFees), FormatMoney(pindex->nMint), FormatMoney(nAmountZerocoinSpent)); - - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) - return error("Connect() : WriteBlockIndex for pindex failed"); - - int64_t nTime1 = GetTimeMicros(); - nTimeConnect += nTime1 - nTimeStart; - LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs - 1), nTimeConnect * 0.000001); - - //PoW phase redistributed fees to miner. PoS stage destroys fees. - CAmount nExpectedMint = GetBlockValue(pindex->pprev->nHeight); - if (block.IsProofOfWork()) - nExpectedMint += nFees; - - if (!IsBlockValueValid(block, nExpectedMint, pindex->nMint)) { - return state.DoS(100, - error("ConnectBlock() : reward pays too much (actual=%s vs limit=%s)", - FormatMoney(pindex->nMint), FormatMoney(nExpectedMint)), - REJECT_INVALID, "bad-cb-amount"); - } - - // zerocoin accumulator: if a new accumulator checkpoint was generated, check that it is the correct value - if (!fVerifyingBlocks && pindex->nHeight >= Params().Zerocoin_StartHeight() && pindex->nHeight % 10 == 0) { - uint256 nCheckpointCalculated = 0; - if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated)) - return state.DoS(100, error("ConnectBlock() : failed to calculate accumulator checkpoint")); - - if (nCheckpointCalculated != block.nAccumulatorCheckpoint) { - LogPrintf("%s: block=%d calculated: %s\n block: %s\n", __func__, pindex->nHeight, nCheckpointCalculated.GetHex(), block.nAccumulatorCheckpoint.GetHex()); - return state.DoS(100, error("ConnectBlock() : accumulator does not match calculated value")); - } - } - else if (!fVerifyingBlocks) { - if (block.nAccumulatorCheckpoint != pindex->pprev->nAccumulatorCheckpoint) { - return state.DoS(100, error("ConnectBlock() : new accumulator checkpoint generated on a block that is not multiple of 10")); - } - } - - if (!control.Wait()) - return state.DoS(100, false); - int64_t nTime2 = GetTimeMicros(); - nTimeVerify += nTime2 - nTimeStart; - LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs - 1), nTimeVerify * 0.000001); - - if (fJustCheck) - return true; - - // Write undo information to disk - if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(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->RaiseValidity(BLOCK_VALID_SCRIPTS); - setDirtyBlockIndex.insert(pindex); - } - - if (fTxIndex) - if (!pblocktree->WriteTxIndex(vPos)) - return state.Abort("Failed to write transaction index"); - - // add this block to the view's block chain - view.SetBestBlock(pindex->GetBlockHash()); - - int64_t nTime3 = GetTimeMicros(); - nTimeIndex += nTime3 - nTime2; - LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); - - // Watch for changes to the previous coinbase transaction. - static uint256 hashPrevBestCoinBase; - g_signals.UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = block.vtx[0].GetHash(); - - int64_t nTime4 = GetTimeMicros(); - nTimeCallbacks += nTime4 - nTime3; - LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); + AssertLockHeld(cs_main); + // Check it again in case a previous version let a bad block in + if (!fAlreadyChecked && !CheckBlock(block, state, !fJustCheck, !fJustCheck)) + return false; + + // verify that the view's current state corresponds to the previous block + uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash(); + if (hashPrevBlock != view.GetBestBlock()) + LogPrintf("%s: hashPrev=%s view=%s\n", __func__, hashPrevBlock.ToString().c_str(), view.GetBestBlock().ToString().c_str()); + assert(hashPrevBlock == view.GetBestBlock()); + + // Special case for the genesis block, skipping connection of its transactions + // (its coinbase is unspendable) + if (block.GetHash() == Params().HashGenesisBlock()) { + view.SetBestBlock(pindex->GetBlockHash()); + return true; + } + + if (pindex->nHeight <= Params().LAST_POW_BLOCK() && block.IsProofOfStake()) + return state.DoS(100, error("ConnectBlock() : PoS period not active"), + REJECT_INVALID, "PoS-early"); + + if (pindex->nHeight > Params().LAST_POW_BLOCK() && block.IsProofOfWork()) + return state.DoS(100, error("ConnectBlock() : PoW period ended"), + REJECT_INVALID, "PoW-ended"); + + 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) { + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + const CCoins* coins = view.AccessCoins(tx.GetHash()); + if (coins && !coins->IsPruned()) + return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), + REJECT_INVALID, "bad-txns-BIP30"); + } + } + + // BIP16 didn't become active until Apr 1 2012 + int64_t nBIP16SwitchTime = 1333238400; + bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); + + unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; + + // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: + if (block.nVersion >= 3 && CBlockIndex::IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { + flags |= SCRIPT_VERIFY_DERSIG; + } + + CBlockUndo blockundo; + + CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + + int64_t nTimeStart = GetTimeMicros(); + CAmount nFees = 0; + int nInputs = 0; + unsigned int nSigOps = 0; + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); + std::vector > vPos; + vPos.reserve(block.vtx.size()); + blockundo.vtxundo.reserve(block.vtx.size() - 1); + CAmount nValueOut = 0; + CAmount nValueIn = 0; + unsigned int nMaxBlockSigOps = MAX_BLOCK_SIGOPS_CURRENT; + for (unsigned int i = 0; i < block.vtx.size(); i++) { + const CTransaction& tx = block.vtx[i]; + + nInputs += tx.vin.size(); + nSigOps += GetLegacySigOpCount(tx); + if (nSigOps > nMaxBlockSigOps) + return state.DoS(100, error("ConnectBlock() : too many sigops"), + REJECT_INVALID, "bad-blk-sigops"); + + //Temporarily disable zerocoin transactions for maintenance + if (block.nTime > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && !IsInitialBlockDownload() && tx.ContainsZerocoins()) + return state.DoS(100, error("ConnectBlock() : zerocoin transactions are currently in maintenance mode")); + + if (tx.IsZerocoinSpend()) { + int nHeightTx = 0; + if (IsTransactionInChain(tx.GetHash(), nHeightTx)) { + //when verifying blocks on init, the blocks are scanned without being disconnected - prevent that from causing an error + if (!fVerifyingBlocks || (fVerifyingBlocks && pindex->nHeight > nHeightTx)) + return state.DoS(100, error("%s : txid %s already exists in block %d , trying to include it again in block %d", __func__, tx.GetHash().GetHex(), nHeightTx, pindex->nHeight), + REJECT_INVALID, "bad-txns-inputs-missingorspent"); + } - return true; + //Check for double spending of serial #'s + for (const CTxIn& txIn : tx.vin) { + if (!txIn.scriptSig.IsZerocoinSpend()) + continue; + CoinSpend spend = TxInToZerocoinSpend(txIn); + nValueIn += spend.getDenomination() * COIN; + + // Make sure that the serial number is in valid range + if (!spend.HasValidSerial(Params().Zerocoin_Params())) { + string strError = strprintf("%s : txid=%s in block %d contains invalid serial %s\n", __func__, tx.GetHash().GetHex(), pindex->nHeight, spend.getCoinSerialNumber()); + if (pindex->nHeight >= Params().Zerocoin_Block_EnforceSerialRange()) + return state.DoS(100, error(strError.c_str())); + strError = "NOT ENFORCING : " + strError; + LogPrintf(strError.c_str()); + } + + //Is the serial already in the blockchain? + uint256 hashTxFromDB; + int nHeightTxSpend = 0; + if (zerocoinDB->ReadCoinSpend(spend.getCoinSerialNumber(), hashTxFromDB)) { + if (IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTxSpend)) { + if (!fVerifyingBlocks || (fVerifyingBlocks && pindex->nHeight > nHeightTxSpend)) + return state.DoS(100, error("%s : zCcbc with serial %s is already in the block %d\n", + __func__, spend.getCoinSerialNumber().GetHex(), nHeightTxSpend)); + } + } + + //record spend to database + if (!zerocoinDB->WriteCoinSpend(spend.getCoinSerialNumber(), tx.GetHash())) + return error("%s : failed to record coin serial to database"); + } + } else if (!tx.IsCoinBase()) { + if (!view.HaveInputs(tx)) + return state.DoS(100, error("ConnectBlock() : inputs missing/spent"), + REJECT_INVALID, "bad-txns-inputs-missingorspent"); + + 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 += GetP2SHSigOpCount(tx, view); + if (nSigOps > nMaxBlockSigOps) + return state.DoS(100, error("ConnectBlock() : too many sigops"), + REJECT_INVALID, "bad-blk-sigops"); + } + + if (!tx.IsCoinStake()) + nFees += view.GetValueIn(tx) - tx.GetValueOut(); + nValueIn += view.GetValueIn(tx); + + std::vector vChecks; + if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) + return false; + control.Add(vChecks); + } + nValueOut += tx.GetValueOut(); + + CTxUndo undoDummy; + if (i > 0) { + blockundo.vtxundo.push_back(CTxUndo()); + } + UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + + vPos.push_back(std::make_pair(tx.GetHash(), pos)); + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); + } + + std::list listMints; + BlockToZerocoinMintList(block, listMints); + std::list listSpends = ZerocoinSpendListFromBlock(block); + + if (!fVerifyingBlocks && pindex->nHeight == Params().Zerocoin_StartHeight() + 1) { + RecalculateZCCBCMinted(); + RecalculateZCCBCSpent(); + RecalculateCCBCSupply(1); + } + + // Initialize zerocoin supply to the supply from previous block + if (pindex->pprev && pindex->pprev->GetBlockHeader().nVersion > 3) { + for (auto& denom : zerocoinDenomList) { + pindex->mapZerocoinSupply.at(denom) = pindex->pprev->mapZerocoinSupply.at(denom); + } + } + + // Track zerocoin money supply + CAmount nAmountZerocoinSpent = 0; + pindex->vMintDenominationsInBlock.clear(); + if (pindex->pprev) { + for (auto& m : listMints) { + libzerocoin::CoinDenomination denom = m.GetDenomination(); + pindex->vMintDenominationsInBlock.push_back(m.GetDenomination()); + pindex->mapZerocoinSupply.at(denom)++; + } + + for (auto& denom : listSpends) { + pindex->mapZerocoinSupply.at(denom)--; + nAmountZerocoinSpent += libzerocoin::ZerocoinDenominationToAmount(denom); + + // zerocoin failsafe + if (pindex->mapZerocoinSupply.at(denom) < 0) + return state.DoS(100, error("Block contains zerocoins that spend more than are in the available supply to spend")); + } + } + + for (auto& denom : zerocoinDenomList) { + LogPrint("zero" + "%s coins for denomination %d pubcoin %s\n", + __func__, pindex->mapZerocoinSupply.at(denom), denom); + } + + // track money supply and mint amount info + CAmount nMoneySupplyPrev = pindex->pprev ? pindex->pprev->nMoneySupply : 0; + pindex->nMoneySupply = nMoneySupplyPrev + nValueOut - nValueIn; + pindex->nMint = pindex->nMoneySupply - nMoneySupplyPrev + nFees; + + // LogPrintf("XX69----------> ConnectBlock(): nValueOut: %s, nValueIn: %s, nFees: %s, nMint: %s zCcbcSpent: %s\n", + // FormatMoney(nValueOut), FormatMoney(nValueIn), + // FormatMoney(nFees), FormatMoney(pindex->nMint), FormatMoney(nAmountZerocoinSpent)); + + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) + return error("Connect() : WriteBlockIndex for pindex failed"); + + int64_t nTime1 = GetTimeMicros(); + nTimeConnect += nTime1 - nTimeStart; + LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs - 1), nTimeConnect * 0.000001); + + //PoW phase redistributed fees to miner. PoS stage destroys fees. + CAmount nExpectedMint = GetBlockValue(pindex->pprev->nHeight); + if (block.IsProofOfWork()) + nExpectedMint += nFees; + + if (!IsBlockValueValid(block, nExpectedMint, pindex->nMint)) { + return state.DoS(100, + error("ConnectBlock() : reward pays too much (actual=%s vs limit=%s)", + FormatMoney(pindex->nMint), FormatMoney(nExpectedMint)), + REJECT_INVALID, "bad-cb-amount"); + } + + // zerocoin accumulator: if a new accumulator checkpoint was generated, check that it is the correct value + if (!fVerifyingBlocks && pindex->nHeight >= Params().Zerocoin_StartHeight() && pindex->nHeight % 10 == 0) { + uint256 nCheckpointCalculated = 0; + if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated)) + return state.DoS(100, error("ConnectBlock() : failed to calculate accumulator checkpoint")); + + if (nCheckpointCalculated != block.nAccumulatorCheckpoint) { + LogPrintf("%s: block=%d calculated: %s\n block: %s\n", __func__, pindex->nHeight, nCheckpointCalculated.GetHex(), block.nAccumulatorCheckpoint.GetHex()); + return state.DoS(100, error("ConnectBlock() : accumulator does not match calculated value")); + } + } else if (!fVerifyingBlocks) { + if (block.nAccumulatorCheckpoint != pindex->pprev->nAccumulatorCheckpoint) { + return state.DoS(100, error("ConnectBlock() : new accumulator checkpoint generated on a block that is not multiple of 10")); + } + } + + if (!control.Wait()) + return state.DoS(100, false); + int64_t nTime2 = GetTimeMicros(); + nTimeVerify += nTime2 - nTimeStart; + LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs - 1), nTimeVerify * 0.000001); + + if (fJustCheck) + return true; + + // Write undo information to disk + if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(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->RaiseValidity(BLOCK_VALID_SCRIPTS); + setDirtyBlockIndex.insert(pindex); + } + + if (fTxIndex) + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort("Failed to write transaction index"); + + // add this block to the view's block chain + view.SetBestBlock(pindex->GetBlockHash()); + + int64_t nTime3 = GetTimeMicros(); + nTimeIndex += nTime3 - nTime2; + LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); + + // Watch for changes to the previous coinbase transaction. + static uint256 hashPrevBestCoinBase; + g_signals.UpdatedTransaction(hashPrevBestCoinBase); + hashPrevBestCoinBase = block.vtx[0].GetHash(); + + int64_t nTime4 = GetTimeMicros(); + nTimeCallbacks += nTime4 - nTime3; + LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); + + return true; } enum FlushStateMode { - FLUSH_STATE_IF_NEEDED, - FLUSH_STATE_PERIODIC, - FLUSH_STATE_ALWAYS + FLUSH_STATE_IF_NEEDED, + FLUSH_STATE_PERIODIC, + FLUSH_STATE_ALWAYS }; /** -* Update the on-disk chain state. -* The caches and indexes are flushed if either they're too large, forceWrite is set, or -* fast is not set and it's been a while since the last write. -*/ + * Update the on-disk chain state. + * The caches and indexes are flushed if either they're too large, forceWrite is set, or + * fast is not set and it's been a while since the last write. + */ bool static FlushStateToDisk(CValidationState& state, FlushStateMode mode) { - LOCK(cs_main); - static int64_t nLastWrite = 0; - try { - if ((mode == FLUSH_STATE_ALWAYS) || - ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || - (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000)) { - // 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("out of disk space"); - // First make sure all block and undo data is flushed to disk. - FlushBlockFile(); - // Then update all block file information (which may refer to block and undo files). - bool fileschanged = false; - for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end();) { - if (!pblocktree->WriteBlockFileInfo(*it, vinfoBlockFile[*it])) { - return state.Abort("Failed to write to block index"); - } - fileschanged = true; - setDirtyFileInfo.erase(it++); - } - if (fileschanged && !pblocktree->WriteLastBlockFile(nLastBlockFile)) { - return state.Abort("Failed to write to block index"); - } - for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end();) { - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(*it))) { - return state.Abort("Failed to write to block index"); - } - setDirtyBlockIndex.erase(it++); - } - pblocktree->Sync(); - // Finally flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return state.Abort("Failed to write to coin database"); - // Update best block in wallet (so we can detect restored wallets). - if (mode != FLUSH_STATE_IF_NEEDED) { - g_signals.SetBestChain(chainActive.GetLocator()); - } - nLastWrite = GetTimeMicros(); - } - } - catch (const std::runtime_error& e) { - return state.Abort(std::string("System error while flushing: ") + e.what()); - } - return true; + LOCK(cs_main); + static int64_t nLastWrite = 0; + try { + if ((mode == FLUSH_STATE_ALWAYS) || + ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || + (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000)) { + // 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("out of disk space"); + // First make sure all block and undo data is flushed to disk. + FlushBlockFile(); + // Then update all block file information (which may refer to block and undo files). + bool fileschanged = false; + for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end();) { + if (!pblocktree->WriteBlockFileInfo(*it, vinfoBlockFile[*it])) { + return state.Abort("Failed to write to block index"); + } + fileschanged = true; + setDirtyFileInfo.erase(it++); + } + if (fileschanged && !pblocktree->WriteLastBlockFile(nLastBlockFile)) { + return state.Abort("Failed to write to block index"); + } + for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end();) { + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(*it))) { + return state.Abort("Failed to write to block index"); + } + setDirtyBlockIndex.erase(it++); + } + pblocktree->Sync(); + // Finally flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return state.Abort("Failed to write to coin database"); + // Update best block in wallet (so we can detect restored wallets). + if (mode != FLUSH_STATE_IF_NEEDED) { + g_signals.SetBestChain(chainActive.GetLocator()); + } + nLastWrite = GetTimeMicros(); + } + } catch (const std::runtime_error& e) { + return state.Abort(std::string("System error while flushing: ") + e.what()); + } + return true; } void FlushStateToDisk() { - CValidationState state; - FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + CValidationState state; + FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } /** Update chainActive and related internal data structures. */ void static UpdateTip(CBlockIndex* pindexNew) { - chainActive.SetTip(pindexNew); - - // If turned on AutoZeromint will automatically convert CCBC to zCCBC - if (pwalletMain->isZeromintEnabled()) - pwalletMain->AutoZeromint(); - - // New best block - nTimeBestReceived = GetTime(); - mempool.AddTransactionsUpdated(1); - - LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), (unsigned long)chainActive.Tip()->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); - - cvBlockChange.notify_all(); - - // Check the version of the last 100 blocks to see if we need to upgrade: - static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) { - int nUpgraded = 0; - const CBlockIndex* pindex = chainActive.Tip(); - for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) - ++nUpgraded; - pindex = pindex->pprev; - } - if (nUpgraded > 0) - LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)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!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; - } - } + chainActive.SetTip(pindexNew); + + // If turned on AutoZeromint will automatically convert CCBC to zCCBC + if (pwalletMain->isZeromintEnabled()) + pwalletMain->AutoZeromint(); + + // New best block + nTimeBestReceived = GetTime(); + mempool.AddTransactionsUpdated(1); + + LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); + + cvBlockChange.notify_all(); + + // Check the version of the last 100 blocks to see if we need to upgrade: + static bool fWarned = false; + if (!IsInitialBlockDownload() && !fWarned) { + int nUpgraded = 0; + const CBlockIndex* pindex = chainActive.Tip(); + for (int i = 0; i < 100 && pindex != NULL; i++) { + if (pindex->nVersion > CBlock::CURRENT_VERSION) + ++nUpgraded; + pindex = pindex->pprev; + } + if (nUpgraded > 0) + LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)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!"); + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } + } } /** Disconnect chainActive's tip. */ bool static DisconnectTip(CValidationState& state) { - CBlockIndex* pindexDelete = chainActive.Tip(); - assert(pindexDelete); - mempool.check(pcoinsTip); - // Read block from disk. - CBlock block; - if (!ReadBlockFromDisk(block, pindexDelete)) - return state.Abort("Failed to read block"); - // Apply the block atomically to the chain state. - int64_t nStart = GetTimeMicros(); - { - CCoinsViewCache view(pcoinsTip); - if (!DisconnectBlock(block, state, pindexDelete, view)) - return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); - assert(view.Flush()); - } - LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) - return false; - // Resurrect mempool transactions from the disconnected block. - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - // ignore validation errors in resurrected transactions - list removed; - CValidationState stateDummy; - if (tx.IsCoinBase() || tx.IsCoinStake() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) - mempool.remove(tx, removed, true); - } - mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); - mempool.check(pcoinsTip); - // Update chainActive and related variables. - UpdateTip(pindexDelete->pprev); - // Let wallets know transactions went from 1-confirmed to - // 0-confirmed or conflicted: - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - SyncWithWallets(tx, NULL); - } - return true; + CBlockIndex* pindexDelete = chainActive.Tip(); + assert(pindexDelete); + mempool.check(pcoinsTip); + // Read block from disk. + CBlock block; + if (!ReadBlockFromDisk(block, pindexDelete)) + return state.Abort("Failed to read block"); + // Apply the block atomically to the chain state. + int64_t nStart = GetTimeMicros(); + { + CCoinsViewCache view(pcoinsTip); + if (!DisconnectBlock(block, state, pindexDelete, view)) + return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); + assert(view.Flush()); + } + LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + // Write the chain state to disk, if necessary. + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) + return false; + // Resurrect mempool transactions from the disconnected block. + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + // ignore validation errors in resurrected transactions + list removed; + CValidationState stateDummy; + if (tx.IsCoinBase() || tx.IsCoinStake() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + mempool.remove(tx, removed, true); + } + mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); + mempool.check(pcoinsTip); + // Update chainActive and related variables. + UpdateTip(pindexDelete->pprev); + // Let wallets know transactions went from 1-confirmed to + // 0-confirmed or conflicted: + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + SyncWithWallets(tx, NULL); + } + return true; } static int64_t nTimeReadFromDisk = 0; @@ -3718,1034 +3571,1023 @@ static int64_t nTimeChainState = 0; static int64_t nTimePostConnect = 0; /** -* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock -* corresponding to pindexNew, to bypass loading it again from disk. -*/ + * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock + * corresponding to pindexNew, to bypass loading it again from disk. + */ bool static ConnectTip(CValidationState& state, CBlockIndex* pindexNew, CBlock* pblock, bool fAlreadyChecked) { - assert(pindexNew->pprev == chainActive.Tip()); - mempool.check(pcoinsTip); - CCoinsViewCache view(pcoinsTip); - - if (pblock == NULL) - fAlreadyChecked = false; - - // Read block from disk. - int64_t nTime1 = GetTimeMicros(); - CBlock block; - if (!pblock) { - if (!ReadBlockFromDisk(block, pindexNew)) - return state.Abort("Failed to read block"); - pblock = █ - } - // Apply the block atomically to the chain state. - int64_t nTime2 = GetTimeMicros(); - nTimeReadFromDisk += nTime2 - nTime1; - int64_t nTime3; - LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); - { - CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); - bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, fAlreadyChecked); - g_signals.BlockChecked(*pblock, state); - if (!rv) { - if (state.IsInvalid()) - InvalidBlockFound(pindexNew, state); - return error("ConnectTip() : ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); - } - mapBlockSource.erase(inv.hash); - nTime3 = GetTimeMicros(); - nTimeConnectTotal += nTime3 - nTime2; - LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); - assert(view.Flush()); - } - int64_t nTime4 = GetTimeMicros(); - nTimeFlush += nTime4 - nTime3; - LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); - - // Write the chain state to disk, if necessary. Always write to disk if this is the first of a new file. - FlushStateMode flushMode = FLUSH_STATE_IF_NEEDED; - if (pindexNew->pprev && (pindexNew->GetBlockPos().nFile != pindexNew->pprev->GetBlockPos().nFile)) - flushMode = FLUSH_STATE_ALWAYS; - if (!FlushStateToDisk(state, flushMode)) - return false; - int64_t nTime5 = GetTimeMicros(); - nTimeChainState += nTime5 - nTime4; - LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); - - // Remove conflicting transactions from the mempool. - list txConflicted; - mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted); - mempool.check(pcoinsTip); - // Update chainActive & related variables. - UpdateTip(pindexNew); - // Tell wallet about transactions that went from mempool - // to conflicted: - BOOST_FOREACH(const CTransaction& tx, txConflicted) { - SyncWithWallets(tx, NULL); - } - // ... and about transactions that got confirmed: - BOOST_FOREACH(const CTransaction& tx, pblock->vtx) { - SyncWithWallets(tx, pblock); - } - - int64_t nTime6 = GetTimeMicros(); - nTimePostConnect += nTime6 - nTime5; - nTimeTotal += nTime6 - nTime1; - LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); - LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); - return true; + assert(pindexNew->pprev == chainActive.Tip()); + mempool.check(pcoinsTip); + CCoinsViewCache view(pcoinsTip); + + if (pblock == NULL) + fAlreadyChecked = false; + + // Read block from disk. + int64_t nTime1 = GetTimeMicros(); + CBlock block; + if (!pblock) { + if (!ReadBlockFromDisk(block, pindexNew)) + return state.Abort("Failed to read block"); + pblock = █ + } + // Apply the block atomically to the chain state. + int64_t nTime2 = GetTimeMicros(); + nTimeReadFromDisk += nTime2 - nTime1; + int64_t nTime3; + LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + { + CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, fAlreadyChecked); + g_signals.BlockChecked(*pblock, state); + if (!rv) { + if (state.IsInvalid()) + InvalidBlockFound(pindexNew, state); + return error("ConnectTip() : ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); + } + mapBlockSource.erase(inv.hash); + nTime3 = GetTimeMicros(); + nTimeConnectTotal += nTime3 - nTime2; + LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); + assert(view.Flush()); + } + int64_t nTime4 = GetTimeMicros(); + nTimeFlush += nTime4 - nTime3; + LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); + + // Write the chain state to disk, if necessary. Always write to disk if this is the first of a new file. + FlushStateMode flushMode = FLUSH_STATE_IF_NEEDED; + if (pindexNew->pprev && (pindexNew->GetBlockPos().nFile != pindexNew->pprev->GetBlockPos().nFile)) + flushMode = FLUSH_STATE_ALWAYS; + if (!FlushStateToDisk(state, flushMode)) + return false; + int64_t nTime5 = GetTimeMicros(); + nTimeChainState += nTime5 - nTime4; + LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); + + // Remove conflicting transactions from the mempool. + list txConflicted; + mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted); + mempool.check(pcoinsTip); + // Update chainActive & related variables. + UpdateTip(pindexNew); + // Tell wallet about transactions that went from mempool + // to conflicted: + BOOST_FOREACH (const CTransaction& tx, txConflicted) { + SyncWithWallets(tx, NULL); + } + // ... and about transactions that got confirmed: + BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { + SyncWithWallets(tx, pblock); + } + + int64_t nTime6 = GetTimeMicros(); + nTimePostConnect += nTime6 - nTime5; + nTimeTotal += nTime6 - nTime1; + LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); + LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); + return true; } bool DisconnectBlocksAndReprocess(int blocks) { - LOCK(cs_main); + LOCK(cs_main); - CValidationState state; + CValidationState state; - LogPrintf("DisconnectBlocksAndReprocess: Got command to replay %d blocks\n", blocks); - for (int i = 0; i <= blocks; i++) - DisconnectTip(state); + LogPrintf("DisconnectBlocksAndReprocess: Got command to replay %d blocks\n", blocks); + for (int i = 0; i <= blocks; i++) + DisconnectTip(state); - return true; + return true; } /* -DisconnectBlockAndInputs - -Remove conflicting blocks for successful SwiftX transaction locks -This should be very rare (Probably will never happen) + DisconnectBlockAndInputs + Remove conflicting blocks for successful SwiftX transaction locks + This should be very rare (Probably will never happen) */ // ***TODO*** clean up here bool DisconnectBlockAndInputs(CValidationState& state, CTransaction txLock) { - // All modifications to the coin state will be done in this cache. - // Only when all have succeeded, we push it to pcoinsTip. - // CCoinsViewCache view(*pcoinsTip, true); - - CBlockIndex* BlockReading = chainActive.Tip(); - CBlockIndex* pindexNew = NULL; - - bool foundConflictingTx = false; - - //remove anything conflicting in the memory pool - list txConflicted; - mempool.removeConflicts(txLock, txConflicted); - - - // List of what to disconnect (typically nothing) - vector vDisconnect; - - for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0 && !foundConflictingTx && i < 6; i++) { - vDisconnect.push_back(BlockReading); - pindexNew = BlockReading->pprev; //new best block - - CBlock block; - if (!ReadBlockFromDisk(block, BlockReading)) - return state.Abort(_("Failed to read block")); - - // 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()) { - BOOST_FOREACH(const CTxIn& in1, txLock.vin) { - BOOST_FOREACH(const CTxIn& in2, tx.vin) { - if (in1.prevout == in2.prevout) foundConflictingTx = true; - } - } - } - } - - if (BlockReading->pprev == NULL) { - assert(BlockReading); - break; - } - BlockReading = BlockReading->pprev; - } - - if (!foundConflictingTx) { - LogPrintf("DisconnectBlockAndInputs: Can't find a conflicting transaction to inputs\n"); - return false; - } - - if (vDisconnect.size() > 0) { - LogPrintf("REORGANIZE: Disconnect Conflicting Blocks %lli blocks; %s..\n", vDisconnect.size(), pindexNew->GetBlockHash().ToString()); - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { - LogPrintf(" -- disconnect %s\n", pindex->GetBlockHash().ToString()); - DisconnectTip(state); - } - } + // All modifications to the coin state will be done in this cache. + // Only when all have succeeded, we push it to pcoinsTip. + // CCoinsViewCache view(*pcoinsTip, true); - return true; + CBlockIndex* BlockReading = chainActive.Tip(); + CBlockIndex* pindexNew = NULL; + + bool foundConflictingTx = false; + + //remove anything conflicting in the memory pool + list txConflicted; + mempool.removeConflicts(txLock, txConflicted); + + + // List of what to disconnect (typically nothing) + vector vDisconnect; + + for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0 && !foundConflictingTx && i < 6; i++) { + vDisconnect.push_back(BlockReading); + pindexNew = BlockReading->pprev; //new best block + + CBlock block; + if (!ReadBlockFromDisk(block, BlockReading)) + return state.Abort(_("Failed to read block")); + + // 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()) { + BOOST_FOREACH (const CTxIn& in1, txLock.vin) { + BOOST_FOREACH (const CTxIn& in2, tx.vin) { + if (in1.prevout == in2.prevout) foundConflictingTx = true; + } + } + } + } + + if (BlockReading->pprev == NULL) { + assert(BlockReading); + break; + } + BlockReading = BlockReading->pprev; + } + + if (!foundConflictingTx) { + LogPrintf("DisconnectBlockAndInputs: Can't find a conflicting transaction to inputs\n"); + return false; + } + + if (vDisconnect.size() > 0) { + LogPrintf("REORGANIZE: Disconnect Conflicting Blocks %lli blocks; %s..\n", vDisconnect.size(), pindexNew->GetBlockHash().ToString()); + BOOST_FOREACH (CBlockIndex* pindex, vDisconnect) { + LogPrintf(" -- disconnect %s\n", pindex->GetBlockHash().ToString()); + DisconnectTip(state); + } + } + + return true; } /** -* Return the tip of the chain with the most work in it, that isn't -* known to be invalid (it's however far from certain to be valid). -*/ + * Return the tip of the chain with the most work in it, that isn't + * known to be invalid (it's however far from certain to be valid). + */ static CBlockIndex* FindMostWorkChain() { - do { - CBlockIndex* pindexNew = NULL; - - // Find the best candidate header. - { - std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); - if (it == setBlockIndexCandidates.rend()) - return NULL; - pindexNew = *it; - } - - // Check whether all blocks on the path between the currently active chain and the candidate are valid. - // Just going until the active chain is an optimization, as we know all blocks in it are valid already. - CBlockIndex* pindexTest = pindexNew; - bool fInvalidAncestor = false; - while (pindexTest && !chainActive.Contains(pindexTest)) { - assert(pindexTest->nChainTx || pindexTest->nHeight == 0); - - // Pruned nodes may have entries in setBlockIndexCandidates for - // which block files have been deleted. Remove those as candidates - // for the most work chain if we come across them; we can't switch - // to a chain unless we have all the non-active-chain parent blocks. - bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; - bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); - if (fFailedChain || fMissingData) { - // Candidate chain is not usable (either invalid or missing data) - if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) - pindexBestInvalid = pindexNew; - CBlockIndex* pindexFailed = pindexNew; - // Remove the entire chain from the set. - while (pindexTest != pindexFailed) { - if (fFailedChain) { - pindexFailed->nStatus |= BLOCK_FAILED_CHILD; - } - else if (fMissingData) { - // If we're missing data, then add back to mapBlocksUnlinked, - // so that if the block arrives in the future we can try adding - // to setBlockIndexCandidates again. - mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); - } - setBlockIndexCandidates.erase(pindexFailed); - pindexFailed = pindexFailed->pprev; - } - setBlockIndexCandidates.erase(pindexTest); - fInvalidAncestor = true; - break; - } - pindexTest = pindexTest->pprev; - } - if (!fInvalidAncestor) - return pindexNew; - } while (true); + do { + CBlockIndex* pindexNew = NULL; + + // Find the best candidate header. + { + std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); + if (it == setBlockIndexCandidates.rend()) + return NULL; + pindexNew = *it; + } + + // Check whether all blocks on the path between the currently active chain and the candidate are valid. + // Just going until the active chain is an optimization, as we know all blocks in it are valid already. + CBlockIndex* pindexTest = pindexNew; + bool fInvalidAncestor = false; + while (pindexTest && !chainActive.Contains(pindexTest)) { + assert(pindexTest->nChainTx || pindexTest->nHeight == 0); + + // Pruned nodes may have entries in setBlockIndexCandidates for + // which block files have been deleted. Remove those as candidates + // for the most work chain if we come across them; we can't switch + // to a chain unless we have all the non-active-chain parent blocks. + bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; + bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); + if (fFailedChain || fMissingData) { + // Candidate chain is not usable (either invalid or missing data) + if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) + pindexBestInvalid = pindexNew; + CBlockIndex* pindexFailed = pindexNew; + // Remove the entire chain from the set. + while (pindexTest != pindexFailed) { + if (fFailedChain) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + } else if (fMissingData) { + // If we're missing data, then add back to mapBlocksUnlinked, + // so that if the block arrives in the future we can try adding + // to setBlockIndexCandidates again. + mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); + } + setBlockIndexCandidates.erase(pindexFailed); + pindexFailed = pindexFailed->pprev; + } + setBlockIndexCandidates.erase(pindexTest); + fInvalidAncestor = true; + break; + } + pindexTest = pindexTest->pprev; + } + if (!fInvalidAncestor) + return pindexNew; + } while (true); } /** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */ static void PruneBlockIndexCandidates() { - // Note that we can't delete the current block itself, as we may need to return to it later in case a - // reorganization to a better block fails. - std::set::iterator it = setBlockIndexCandidates.begin(); - while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { - setBlockIndexCandidates.erase(it++); - } - // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates. - assert(!setBlockIndexCandidates.empty()); + // Note that we can't delete the current block itself, as we may need to return to it later in case a + // reorganization to a better block fails. + std::set::iterator it = setBlockIndexCandidates.begin(); + while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { + setBlockIndexCandidates.erase(it++); + } + // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates. + assert(!setBlockIndexCandidates.empty()); } /** -* Try to make some progress towards making pindexMostWork the active block. -* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. -*/ + * Try to make some progress towards making pindexMostWork the active block. + * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. + */ static bool ActivateBestChainStep(CValidationState& state, CBlockIndex* pindexMostWork, CBlock* pblock, bool fAlreadyChecked) { - AssertLockHeld(cs_main); - if (pblock == NULL) - fAlreadyChecked = false; - bool fInvalidFound = false; - const CBlockIndex* pindexOldTip = chainActive.Tip(); - const CBlockIndex* pindexFork = chainActive.FindFork(pindexMostWork); - - // Disconnect active blocks which are no longer in the best chain. - while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state)) - return false; - } - - // Build list of new blocks to connect. - std::vector vpindexToConnect; - bool fContinue = true; - int nHeight = pindexFork ? pindexFork->nHeight : -1; - while (fContinue && nHeight != pindexMostWork->nHeight) { - // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need - // a few blocks along the way. - int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); - vpindexToConnect.clear(); - vpindexToConnect.reserve(nTargetHeight - nHeight); - CBlockIndex* pindexIter = pindexMostWork->GetAncestor(nTargetHeight); - while (pindexIter && pindexIter->nHeight != nHeight) { - vpindexToConnect.push_back(pindexIter); - pindexIter = pindexIter->pprev; - } - nHeight = nTargetHeight; - - // Connect new blocks. - BOOST_REVERSE_FOREACH(CBlockIndex* pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, fAlreadyChecked)) { - if (state.IsInvalid()) { - // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); - state = CValidationState(); - fInvalidFound = true; - fContinue = false; - break; - } - else { - // A system error occurred (disk space, database error, ...). - return false; - } - } - else { - PruneBlockIndexCandidates(); - if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { - // We're in a better position than we were. Return temporarily to release the lock. - fContinue = false; - break; - } - } - } - } - - // Callbacks/notifications for a new best chain. - if (fInvalidFound) - CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); - else - CheckForkWarningConditions(); + AssertLockHeld(cs_main); + if (pblock == NULL) + fAlreadyChecked = false; + bool fInvalidFound = false; + const CBlockIndex* pindexOldTip = chainActive.Tip(); + const CBlockIndex* pindexFork = chainActive.FindFork(pindexMostWork); + + // Disconnect active blocks which are no longer in the best chain. + while (chainActive.Tip() && chainActive.Tip() != pindexFork) { + if (!DisconnectTip(state)) + return false; + } + + // Build list of new blocks to connect. + std::vector vpindexToConnect; + bool fContinue = true; + int nHeight = pindexFork ? pindexFork->nHeight : -1; + while (fContinue && nHeight != pindexMostWork->nHeight) { + // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need + // a few blocks along the way. + int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); + vpindexToConnect.clear(); + vpindexToConnect.reserve(nTargetHeight - nHeight); + CBlockIndex* pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + while (pindexIter && pindexIter->nHeight != nHeight) { + vpindexToConnect.push_back(pindexIter); + pindexIter = pindexIter->pprev; + } + nHeight = nTargetHeight; + + // Connect new blocks. + BOOST_REVERSE_FOREACH (CBlockIndex* pindexConnect, vpindexToConnect) { + if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, fAlreadyChecked)) { + if (state.IsInvalid()) { + // The block violates a consensus rule. + if (!state.CorruptionPossible()) + InvalidChainFound(vpindexToConnect.back()); + state = CValidationState(); + fInvalidFound = true; + fContinue = false; + break; + } else { + // A system error occurred (disk space, database error, ...). + return false; + } + } else { + PruneBlockIndexCandidates(); + if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { + // We're in a better position than we were. Return temporarily to release the lock. + fContinue = false; + break; + } + } + } + } - return true; + // Callbacks/notifications for a new best chain. + if (fInvalidFound) + CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); + else + CheckForkWarningConditions(); + + return true; } /** -* Make the best chain active, in multiple steps. The result is either failure -* or an activated best chain. pblock is either NULL or a pointer to a block -* that is already loaded (to avoid loading it again from disk). -*/ + * Make the best chain active, in multiple steps. The result is either failure + * or an activated best chain. pblock is either NULL or a pointer to a block + * that is already loaded (to avoid loading it again from disk). + */ bool ActivateBestChain(CValidationState& state, CBlock* pblock, bool fAlreadyChecked) { - CBlockIndex* pindexNewTip = NULL; - CBlockIndex* pindexMostWork = NULL; - do { - boost::this_thread::interruption_point(); - - bool fInitialDownload; - while (true) { - TRY_LOCK(cs_main, lockMain); - if (!lockMain) { - MilliSleep(50); - continue; - } - - pindexMostWork = FindMostWorkChain(); - - // Whether we have anything to do at all. - if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) - return true; - - if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fAlreadyChecked)) - return false; - - pindexNewTip = chainActive.Tip(); - fInitialDownload = IsInitialBlockDownload(); - break; - } - // When we reach this point, we switched to a new tip (stored in pindexNewTip). - - // Notifications/callbacks that can run without cs_main - if (!fInitialDownload) { - uint256 hashNewTip = pindexNewTip->GetBlockHash(); - // Relay inventory, but don't relay old inventory during initial block download. - int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); - } - // Notify external listeners about the new tip. - uiInterface.NotifyBlockTip(hashNewTip); - } - } while (pindexMostWork != chainActive.Tip()); - CheckBlockIndex(); - - // Write changes periodically to disk, after relay. - if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { - return false; - } + CBlockIndex* pindexNewTip = NULL; + CBlockIndex* pindexMostWork = NULL; + do { + boost::this_thread::interruption_point(); + + bool fInitialDownload; + while (true) { + TRY_LOCK(cs_main, lockMain); + if (!lockMain) { + MilliSleep(50); + continue; + } - return true; + pindexMostWork = FindMostWorkChain(); + + // Whether we have anything to do at all. + if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) + return true; + + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fAlreadyChecked)) + return false; + + pindexNewTip = chainActive.Tip(); + fInitialDownload = IsInitialBlockDownload(); + break; + } + // When we reach this point, we switched to a new tip (stored in pindexNewTip). + + // Notifications/callbacks that can run without cs_main + if (!fInitialDownload) { + uint256 hashNewTip = pindexNewTip->GetBlockHash(); + // Relay inventory, but don't relay old inventory during initial block download. + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); + { + LOCK(cs_vNodes); + BOOST_FOREACH (CNode* pnode, vNodes) + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + } + // Notify external listeners about the new tip. + uiInterface.NotifyBlockTip(hashNewTip); + } + } while (pindexMostWork != chainActive.Tip()); + CheckBlockIndex(); + + // Write changes periodically to disk, after relay. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { + return false; + } + + return true; } bool InvalidateBlock(CValidationState& state, CBlockIndex* pindex) { - AssertLockHeld(cs_main); - - // Mark the block itself as invalid. - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - setBlockIndexCandidates.erase(pindex); - - while (chainActive.Contains(pindex)) { - CBlockIndex* pindexWalk = chainActive.Tip(); - pindexWalk->nStatus |= BLOCK_FAILED_CHILD; - setDirtyBlockIndex.insert(pindexWalk); - setBlockIndexCandidates.erase(pindexWalk); - // ActivateBestChain considers blocks already in chainActive - // unconditionally valid already, so force disconnect away from it. - if (!DisconnectTip(state)) { - return false; - } - } - - // The resulting new best tip may not be in setBlockIndexCandidates anymore, so - // add them again. - BlockMap::iterator it = mapBlockIndex.begin(); - while (it != mapBlockIndex.end()) { - if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { - setBlockIndexCandidates.insert(it->second); - } - it++; - } - - InvalidChainFound(pindex); - return true; + AssertLockHeld(cs_main); + + // Mark the block itself as invalid. + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + setBlockIndexCandidates.erase(pindex); + + while (chainActive.Contains(pindex)) { + CBlockIndex* pindexWalk = chainActive.Tip(); + pindexWalk->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(pindexWalk); + setBlockIndexCandidates.erase(pindexWalk); + // ActivateBestChain considers blocks already in chainActive + // unconditionally valid already, so force disconnect away from it. + if (!DisconnectTip(state)) { + return false; + } + } + + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so + // add them again. + BlockMap::iterator it = mapBlockIndex.begin(); + while (it != mapBlockIndex.end()) { + if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { + setBlockIndexCandidates.insert(it->second); + } + it++; + } + + InvalidChainFound(pindex); + return true; } bool ReconsiderBlock(CValidationState& state, CBlockIndex* pindex) { - AssertLockHeld(cs_main); - - int nHeight = pindex->nHeight; - - // Remove the invalidity flag from this block and all its descendants. - BlockMap::iterator it = mapBlockIndex.begin(); - while (it != mapBlockIndex.end()) { - if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { - it->second->nStatus &= ~BLOCK_FAILED_MASK; - setDirtyBlockIndex.insert(it->second); - if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { - setBlockIndexCandidates.insert(it->second); - } - if (it->second == pindexBestInvalid) { - // Reset invalid block marker if it was pointing to one of those. - pindexBestInvalid = NULL; - } - } - it++; - } - - // Remove the invalidity flag from all ancestors too. - while (pindex != NULL) { - if (pindex->nStatus & BLOCK_FAILED_MASK) { - pindex->nStatus &= ~BLOCK_FAILED_MASK; - setDirtyBlockIndex.insert(pindex); - } - pindex = pindex->pprev; - } - return true; + AssertLockHeld(cs_main); + + int nHeight = pindex->nHeight; + + // Remove the invalidity flag from this block and all its descendants. + BlockMap::iterator it = mapBlockIndex.begin(); + while (it != mapBlockIndex.end()) { + if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { + it->second->nStatus &= ~BLOCK_FAILED_MASK; + setDirtyBlockIndex.insert(it->second); + if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { + setBlockIndexCandidates.insert(it->second); + } + if (it->second == pindexBestInvalid) { + // Reset invalid block marker if it was pointing to one of those. + pindexBestInvalid = NULL; + } + } + it++; + } + + // Remove the invalidity flag from all ancestors too. + while (pindex != NULL) { + if (pindex->nStatus & BLOCK_FAILED_MASK) { + pindex->nStatus &= ~BLOCK_FAILED_MASK; + setDirtyBlockIndex.insert(pindex); + } + pindex = pindex->pprev; + } + return true; } CBlockIndex* AddToBlockIndex(const CBlock& block) { - // Check for duplicate - uint256 hash = block.GetHash(); - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end()) - return it->second; - - // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(block); - assert(pindexNew); - // We assign the sequence id to blocks only when the full data is available, - // to avoid miners withholding blocks but broadcasting headers, to get a - // competitive advantage. - pindexNew->nSequenceId = 0; - BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - - //mark as PoS seen - if (pindexNew->IsProofOfStake()) - setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); - - pindexNew->phashBlock = &((*mi).first); - BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); - if (miPrev != mapBlockIndex.end()) { - pindexNew->pprev = (*miPrev).second; - pindexNew->nHeight = pindexNew->pprev->nHeight + 1; - pindexNew->BuildSkip(); - - //update previous block pointer - pindexNew->pprev->pnext = pindexNew; - - // ppcoin: compute chain trust score - pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust(); - - // ppcoin: compute stake entropy bit for stake modifier - if (!pindexNew->SetStakeEntropyBit(pindexNew->GetStakeEntropyBit())) - LogPrintf("AddToBlockIndex() : SetStakeEntropyBit() failed \n"); - - // ppcoin: record proof-of-stake hash value - if (pindexNew->IsProofOfStake()) { - if (!mapProofOfStake.count(hash)) - LogPrintf("AddToBlockIndex() : hashProofOfStake not found in map \n"); - pindexNew->hashProofOfStake = mapProofOfStake[hash]; - } - - // ppcoin: compute stake modifier - uint64_t nStakeModifier = 0; - bool fGeneratedStakeModifier = false; - if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier)) - LogPrintf("AddToBlockIndex() : ComputeNextStakeModifier() failed \n"); - pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); - pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); - if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) - LogPrintf("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=%s \n", pindexNew->nHeight, boost::lexical_cast(nStakeModifier)); - } - pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); - pindexNew->RaiseValidity(BLOCK_VALID_TREE); - if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) - pindexBestHeader = pindexNew; - - //update previous block pointer - if (pindexNew->nHeight) - pindexNew->pprev->pnext = pindexNew; - - setDirtyBlockIndex.insert(pindexNew); - - return pindexNew; + // Check for duplicate + uint256 hash = block.GetHash(); + BlockMap::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end()) + return it->second; + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(block); + assert(pindexNew); + // We assign the sequence id to blocks only when the full data is available, + // to avoid miners withholding blocks but broadcasting headers, to get a + // competitive advantage. + pindexNew->nSequenceId = 0; + BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + + //mark as PoS seen + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); + + pindexNew->phashBlock = &((*mi).first); + BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); + if (miPrev != mapBlockIndex.end()) { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + pindexNew->BuildSkip(); + + //update previous block pointer + pindexNew->pprev->pnext = pindexNew; + + // ppcoin: compute chain trust score + pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust(); + + // ppcoin: compute stake entropy bit for stake modifier + if (!pindexNew->SetStakeEntropyBit(pindexNew->GetStakeEntropyBit())) + LogPrintf("AddToBlockIndex() : SetStakeEntropyBit() failed \n"); + + // ppcoin: record proof-of-stake hash value + if (pindexNew->IsProofOfStake()) { + if (!mapProofOfStake.count(hash)) + LogPrintf("AddToBlockIndex() : hashProofOfStake not found in map \n"); + pindexNew->hashProofOfStake = mapProofOfStake[hash]; + } + + // ppcoin: compute stake modifier + uint64_t nStakeModifier = 0; + bool fGeneratedStakeModifier = false; + if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier)) + LogPrintf("AddToBlockIndex() : ComputeNextStakeModifier() failed \n"); + pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); + pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); + if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) + LogPrintf("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=%s \n", pindexNew->nHeight, boost::lexical_cast(nStakeModifier)); + } + pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); + pindexNew->RaiseValidity(BLOCK_VALID_TREE); + if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) + pindexBestHeader = pindexNew; + + //update previous block pointer + if (pindexNew->nHeight) + pindexNew->pprev->pnext = pindexNew; + + setDirtyBlockIndex.insert(pindexNew); + + return pindexNew; } /** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */ bool ReceivedBlockTransactions(const CBlock& block, CValidationState& state, CBlockIndex* pindexNew, const CDiskBlockPos& pos) { - if (block.IsProofOfStake()) - pindexNew->SetProofOfStake(); - pindexNew->nTx = block.vtx.size(); - pindexNew->nChainTx = 0; - pindexNew->nFile = pos.nFile; - pindexNew->nDataPos = pos.nPos; - pindexNew->nUndoPos = 0; - pindexNew->nStatus |= BLOCK_HAVE_DATA; - pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); - setDirtyBlockIndex.insert(pindexNew); - - if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { - // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. - deque queue; - queue.push_back(pindexNew); - - // Recursively process any descendant blocks that now may be eligible to be connected. - while (!queue.empty()) { - CBlockIndex* pindex = queue.front(); - queue.pop_front(); - pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; - { - LOCK(cs_nBlockSequenceId); - pindex->nSequenceId = nBlockSequenceId++; - } - if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { - setBlockIndexCandidates.insert(pindex); - } - std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex); - while (range.first != range.second) { - std::multimap::iterator it = range.first; - queue.push_back(it->second); - range.first++; - mapBlocksUnlinked.erase(it); - } - } - } - else { - if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { - mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); - } - } + if (block.IsProofOfStake()) + pindexNew->SetProofOfStake(); + pindexNew->nTx = block.vtx.size(); + pindexNew->nChainTx = 0; + pindexNew->nFile = pos.nFile; + pindexNew->nDataPos = pos.nPos; + pindexNew->nUndoPos = 0; + pindexNew->nStatus |= BLOCK_HAVE_DATA; + pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); + setDirtyBlockIndex.insert(pindexNew); + + if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { + // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. + deque queue; + queue.push_back(pindexNew); + + // Recursively process any descendant blocks that now may be eligible to be connected. + while (!queue.empty()) { + CBlockIndex* pindex = queue.front(); + queue.pop_front(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + { + LOCK(cs_nBlockSequenceId); + pindex->nSequenceId = nBlockSequenceId++; + } + if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { + setBlockIndexCandidates.insert(pindex); + } + std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex); + while (range.first != range.second) { + std::multimap::iterator it = range.first; + queue.push_back(it->second); + range.first++; + mapBlocksUnlinked.erase(it); + } + } + } else { + if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { + mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); + } + } - return true; + return true; } bool FindBlockPos(CValidationState& state, CDiskBlockPos& pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { - LOCK(cs_LastBlockFile); - - unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; - if (vinfoBlockFile.size() <= nFile) { - vinfoBlockFile.resize(nFile + 1); - } - - if (!fKnown) { - while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); - FlushBlockFile(true); - nFile++; - if (vinfoBlockFile.size() <= nFile) { - vinfoBlockFile.resize(nFile + 1); - } - } - pos.nFile = nFile; - pos.nPos = vinfoBlockFile[nFile].nSize; - } - - nLastBlockFile = nFile; - vinfoBlockFile[nFile].AddBlock(nHeight, nTime); - if (fKnown) - vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); - else - vinfoBlockFile[nFile].nSize += nAddSize; - - if (!fKnown) { - unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - unsigned int nNewChunks = (vinfoBlockFile[nFile].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) { - LogPrintf("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("out of disk space"); - } - } - - setDirtyFileInfo.insert(nFile); - return true; + LOCK(cs_LastBlockFile); + + unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; + if (vinfoBlockFile.size() <= nFile) { + vinfoBlockFile.resize(nFile + 1); + } + + if (!fKnown) { + while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); + FlushBlockFile(true); + nFile++; + if (vinfoBlockFile.size() <= nFile) { + vinfoBlockFile.resize(nFile + 1); + } + } + pos.nFile = nFile; + pos.nPos = vinfoBlockFile[nFile].nSize; + } + + nLastBlockFile = nFile; + vinfoBlockFile[nFile].AddBlock(nHeight, nTime); + if (fKnown) + vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); + else + vinfoBlockFile[nFile].nSize += nAddSize; + + if (!fKnown) { + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (vinfoBlockFile[nFile].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) { + LogPrintf("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("out of disk space"); + } + } + + setDirtyFileInfo.insert(nFile); + return true; } bool FindUndoPos(CValidationState& state, int nFile, CDiskBlockPos& pos, unsigned int nAddSize) { - pos.nFile = nFile; - - LOCK(cs_LastBlockFile); - - unsigned int nNewSize; - pos.nPos = vinfoBlockFile[nFile].nUndoSize; - nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; - setDirtyFileInfo.insert(nFile); - - 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) { - LogPrintf("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("out of disk space"); - } + pos.nFile = nFile; - return true; + LOCK(cs_LastBlockFile); + + unsigned int nNewSize; + pos.nPos = vinfoBlockFile[nFile].nUndoSize; + nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; + setDirtyFileInfo.insert(nFile); + + 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) { + LogPrintf("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("out of disk space"); + } + + return true; } bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) { - // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) - return state.DoS(50, error("CheckBlockHeader() : proof of work failed"), - REJECT_INVALID, "high-hash"); + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) + return state.DoS(50, error("CheckBlockHeader() : proof of work failed"), + REJECT_INVALID, "high-hash"); - return true; + return true; } bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) { - // These are checks that are independent of context. - - // Check that the header is valid (particularly PoW). This is mostly - // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) - return state.DoS(100, error("CheckBlock() : CheckBlockHeader failed"), - REJECT_INVALID, "bad-header", true); - - // Check timestamp - LogPrint("debug", "%s: block=%s is proof of stake=%d\n", __func__, block.GetHash().ToString().c_str(), block.IsProofOfStake()); - if (block.GetBlockTime() > GetAdjustedTime() + (block.IsProofOfStake() ? 180 : 7200)) // 3 minute future drift for PoS - return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), - REJECT_INVALID, "time-too-new"); - - // Check the merkle root. - if (fCheckMerkleRoot) { - bool mutated; - uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); - if (block.hashMerkleRoot != hashMerkleRoot2) - return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), - REJECT_INVALID, "bad-txnmrklroot", true); - - // Check for merkle tree malleability (CVE-2012-2459): repeating sequences - // of transactions in a block without affecting the merkle root of a block, - // while still invalidating it. - if (mutated) - return state.DoS(100, error("CheckBlock() : duplicate transaction"), - REJECT_INVALID, "bad-txns-duplicate", true); - } - - // All potential-corruption validation must be done before we do any - // transaction validation, as otherwise we may mark the header as invalid - // because we receive the wrong transactions for it. - - // Size limits - unsigned int nMaxBlockSize = MAX_BLOCK_SIZE_CURRENT; - if (block.vtx.empty() || block.vtx.size() > nMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > nMaxBlockSize) - return state.DoS(100, error("CheckBlock() : size limits failed"), - REJECT_INVALID, "bad-blk-length"); - - // First transaction must be coinbase, the rest must not be - if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), - REJECT_INVALID, "bad-cb-missing"); - for (unsigned int i = 1; i < block.vtx.size(); i++) - if (block.vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : more than one coinbase"), - REJECT_INVALID, "bad-cb-multiple"); - - if (block.IsProofOfStake()) { - // Coinbase output should be empty if proof-of-stake block - if (block.vtx[0].vout.size() != 1 || !block.vtx[0].vout[0].IsEmpty()) - return state.DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block")); - - // Second transaction must be coinstake, the rest must not be - if (block.vtx.empty() || !block.vtx[1].IsCoinStake()) - return state.DoS(100, error("CheckBlock() : second tx is not coinstake")); - for (unsigned int i = 2; i < block.vtx.size(); i++) - if (block.vtx[i].IsCoinStake()) - return state.DoS(100, error("CheckBlock() : more than one coinstake")); - } - - // ----------- swiftTX transaction scanning ----------- - if (IsSporkActive(SPORK_3_SWIFTTX_BLOCK_FILTERING)) { - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - if (!tx.IsCoinBase()) { - //only reject blocks when it's based on complete consensus - BOOST_FOREACH(const CTxIn& in, tx.vin) { - if (mapLockedInputs.count(in.prevout)) { - if (mapLockedInputs[in.prevout] != tx.GetHash()) { - mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime())); - LogPrintf("CheckBlock() : found conflicting transaction with transaction lock %s %s\n", mapLockedInputs[in.prevout].ToString(), tx.GetHash().ToString()); - return state.DoS(0, error("CheckBlock() : found conflicting transaction with transaction lock"), - REJECT_INVALID, "conflicting-tx-ix"); - } - } - } - } - } - } - else { - LogPrintf("CheckBlock() : skipping transaction locking checks\n"); - } - - // masternode payments / budgets and zerocoin check - CBlockIndex* pindexPrev = chainActive.Tip(); - int nHeight = 0; - if (pindexPrev != NULL) { - if (pindexPrev->GetBlockHash() == block.hashPrevBlock) { - nHeight = pindexPrev->nHeight + 1; - } - else { //out of order - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - nHeight = (*mi).second->nHeight + 1; - } - - // Version 4 header must be used after Params().Zerocoin_StartHeight(). And never before. - if (nHeight > Params().Zerocoin_StartHeight()) { - if (block.nVersion < Params().Zerocoin_HeaderVersion()) - return state.DoS(50, error("CheckBlockHeader() : block version must be above 4 after ZerocoinStartHeight"), - REJECT_INVALID, "block-version"); - } - - // Ccbc - // It is entierly possible that we don't have enough data and this could fail - // (i.e. the block could indeed be valid). Store the block for later consideration - // but issue an initial reject message. - // The case also exists that the sending peer could not have enough data to see - // that this block is invalid, so don't issue an outright ban. - if (nHeight != 0 && !IsInitialBlockDownload()) { - if (!IsBlockPayeeValid(block, nHeight)) { - mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime())); - return state.DoS(0, error("CheckBlock() : Couldn't find masternode/budget payment"), - REJECT_INVALID, "bad-cb-payee"); - } - } - else { - if (fDebug) - LogPrintf("CheckBlock(): Masternode payment check skipped on sync - skipping IsBlockPayeeValid()\n"); - } - } - - // Check transactions - bool fZerocoinActive = true; - vector vBlockSerials; - for (const CTransaction& tx : block.vtx) { - if (!CheckTransaction(tx, fZerocoinActive, chainActive.Height() + 1 >= Params().Zerocoin_StartHeight(), state)) - return error("CheckBlock() : CheckTransaction failed"); - - // double check that there are no double spent zCcbc spends in this block - if (tx.IsZerocoinSpend()) { - for (const CTxIn txIn : tx.vin) { - if (txIn.scriptSig.IsZerocoinSpend()) { - libzerocoin::CoinSpend spend = TxInToZerocoinSpend(txIn); - if (count(vBlockSerials.begin(), vBlockSerials.end(), spend.getCoinSerialNumber())) - return state.DoS(100, error("%s : Double spending of zCcbc serial %s in block\n Block: %s", - __func__, spend.getCoinSerialNumber().GetHex(), block.ToString())); - vBlockSerials.emplace_back(spend.getCoinSerialNumber()); - } - } - } - } - - - unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - nSigOps += GetLegacySigOpCount(tx); - } - unsigned int nMaxBlockSigOps = fZerocoinActive ? MAX_BLOCK_SIGOPS_CURRENT : MAX_BLOCK_SIGOPS_LEGACY; - if (nSigOps > nMaxBlockSigOps) - return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), - REJECT_INVALID, "bad-blk-sigops", true); + // These are checks that are independent of context. + + // Check that the header is valid (particularly PoW). This is mostly + // redundant with the call in AcceptBlockHeader. + if (!CheckBlockHeader(block, state, fCheckPOW)) + return state.DoS(100, error("CheckBlock() : CheckBlockHeader failed"), + REJECT_INVALID, "bad-header", true); + + // Check timestamp + LogPrint("debug", "%s: block=%s is proof of stake=%d\n", __func__, block.GetHash().ToString().c_str(), block.IsProofOfStake()); + if (block.GetBlockTime() > GetAdjustedTime() + (block.IsProofOfStake() ? 180 : 7200)) // 3 minute future drift for PoS + return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), + REJECT_INVALID, "time-too-new"); + + // Check the merkle root. + if (fCheckMerkleRoot) { + bool mutated; + uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); + if (block.hashMerkleRoot != hashMerkleRoot2) + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), + REJECT_INVALID, "bad-txnmrklroot", true); + + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences + // of transactions in a block without affecting the merkle root of a block, + // while still invalidating it. + if (mutated) + return state.DoS(100, error("CheckBlock() : duplicate transaction"), + REJECT_INVALID, "bad-txns-duplicate", true); + } + + // All potential-corruption validation must be done before we do any + // transaction validation, as otherwise we may mark the header as invalid + // because we receive the wrong transactions for it. + + // Size limits + unsigned int nMaxBlockSize = MAX_BLOCK_SIZE_CURRENT; + if (block.vtx.empty() || block.vtx.size() > nMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > nMaxBlockSize) + return state.DoS(100, error("CheckBlock() : size limits failed"), + REJECT_INVALID, "bad-blk-length"); + + // First transaction must be coinbase, the rest must not be + if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) + return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), + REJECT_INVALID, "bad-cb-missing"); + for (unsigned int i = 1; i < block.vtx.size(); i++) + if (block.vtx[i].IsCoinBase()) + return state.DoS(100, error("CheckBlock() : more than one coinbase"), + REJECT_INVALID, "bad-cb-multiple"); + + if (block.IsProofOfStake()) { + // Coinbase output should be empty if proof-of-stake block + if (block.vtx[0].vout.size() != 1 || !block.vtx[0].vout[0].IsEmpty()) + return state.DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block")); + + // Second transaction must be coinstake, the rest must not be + if (block.vtx.empty() || !block.vtx[1].IsCoinStake()) + return state.DoS(100, error("CheckBlock() : second tx is not coinstake")); + for (unsigned int i = 2; i < block.vtx.size(); i++) + if (block.vtx[i].IsCoinStake()) + return state.DoS(100, error("CheckBlock() : more than one coinstake")); + } + + // ----------- swiftTX transaction scanning ----------- + if (IsSporkActive(SPORK_3_SWIFTTX_BLOCK_FILTERING)) { + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + if (!tx.IsCoinBase()) { + //only reject blocks when it's based on complete consensus + BOOST_FOREACH (const CTxIn& in, tx.vin) { + if (mapLockedInputs.count(in.prevout)) { + if (mapLockedInputs[in.prevout] != tx.GetHash()) { + mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime())); + LogPrintf("CheckBlock() : found conflicting transaction with transaction lock %s %s\n", mapLockedInputs[in.prevout].ToString(), tx.GetHash().ToString()); + return state.DoS(0, error("CheckBlock() : found conflicting transaction with transaction lock"), + REJECT_INVALID, "conflicting-tx-ix"); + } + } + } + } + } + } else { + LogPrintf("CheckBlock() : skipping transaction locking checks\n"); + } + + // masternode payments / budgets and zerocoin check + CBlockIndex* pindexPrev = chainActive.Tip(); + int nHeight = 0; + if (pindexPrev != NULL) { + if (pindexPrev->GetBlockHash() == block.hashPrevBlock) { + nHeight = pindexPrev->nHeight + 1; + } else { //out of order + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi != mapBlockIndex.end() && (*mi).second) + nHeight = (*mi).second->nHeight + 1; + } + + // Version 4 header must be used after Params().Zerocoin_StartHeight(). And never before. + if (nHeight > Params().Zerocoin_StartHeight()) { + if (block.nVersion < Params().Zerocoin_HeaderVersion()) + return state.DoS(50, error("CheckBlockHeader() : block version must be above 4 after ZerocoinStartHeight"), + REJECT_INVALID, "block-version"); + } + + // Ccbc + // It is entierly possible that we don't have enough data and this could fail + // (i.e. the block could indeed be valid). Store the block for later consideration + // but issue an initial reject message. + // The case also exists that the sending peer could not have enough data to see + // that this block is invalid, so don't issue an outright ban. + if (nHeight != 0 && !IsInitialBlockDownload()) { + if (!IsBlockPayeeValid(block, nHeight)) { + mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime())); + return state.DoS(0, error("CheckBlock() : Couldn't find masternode/budget payment"), + REJECT_INVALID, "bad-cb-payee"); + } + } else { + if (fDebug) + LogPrintf("CheckBlock(): Masternode payment check skipped on sync - skipping IsBlockPayeeValid()\n"); + } + } + + // Check transactions + bool fZerocoinActive = true; + vector vBlockSerials; + for (const CTransaction& tx : block.vtx) { + if (!CheckTransaction(tx, fZerocoinActive, chainActive.Height() + 1 >= Params().Zerocoin_StartHeight(), state)) + return error("CheckBlock() : CheckTransaction failed"); + + // double check that there are no double spent zCcbc spends in this block + if (tx.IsZerocoinSpend()) { + for (const CTxIn txIn : tx.vin) { + if (txIn.scriptSig.IsZerocoinSpend()) { + libzerocoin::CoinSpend spend = TxInToZerocoinSpend(txIn); + if (count(vBlockSerials.begin(), vBlockSerials.end(), spend.getCoinSerialNumber())) + return state.DoS(100, error("%s : Double spending of zCcbc serial %s in block\n Block: %s", + __func__, spend.getCoinSerialNumber().GetHex(), block.ToString())); + vBlockSerials.emplace_back(spend.getCoinSerialNumber()); + } + } + } + } - return true; + + unsigned int nSigOps = 0; + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + nSigOps += GetLegacySigOpCount(tx); + } + unsigned int nMaxBlockSigOps = fZerocoinActive ? MAX_BLOCK_SIGOPS_CURRENT : MAX_BLOCK_SIGOPS_LEGACY; + if (nSigOps > nMaxBlockSigOps) + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), + REJECT_INVALID, "bad-blk-sigops", true); + + return true; } bool CheckWork(const CBlock block, CBlockIndex* const pindexPrev) { - if (pindexPrev == NULL) - return error("%s : null pindexPrev for block %s", __func__, block.GetHash().ToString().c_str()); + if (pindexPrev == NULL) + return error("%s : null pindexPrev for block %s", __func__, block.GetHash().ToString().c_str()); - unsigned int nBitsRequired = GetNextWorkRequired(pindexPrev, &block); + unsigned int nBitsRequired = GetNextWorkRequired(pindexPrev, &block); - if (block.IsProofOfWork() && (pindexPrev->nHeight + 1 <= 68589)) { - double n1 = ConvertBitsToDouble(block.nBits); - double n2 = ConvertBitsToDouble(nBitsRequired); + if (block.IsProofOfWork() && (pindexPrev->nHeight + 1 <= 68589)) { + double n1 = ConvertBitsToDouble(block.nBits); + double n2 = ConvertBitsToDouble(nBitsRequired); - if (abs(n1 - n2) > n1 * 0.5) - return error("%s : incorrect proof of work (DGW pre-fork) - %f %f %f at %d", __func__, abs(n1 - n2), n1, n2, pindexPrev->nHeight + 1); + if (abs(n1 - n2) > n1 * 0.5) + return error("%s : incorrect proof of work (DGW pre-fork) - %f %f %f at %d", __func__, abs(n1 - n2), n1, n2, pindexPrev->nHeight + 1); - return true; - } + return true; + } - if (block.nBits != nBitsRequired) - return error("%s : incorrect proof of work at %d", __func__, pindexPrev->nHeight + 1); + if (block.nBits != nBitsRequired) + return error("%s : incorrect proof of work at %d", __func__, pindexPrev->nHeight + 1); - if (block.IsProofOfStake()) { - uint256 hashProofOfStake; - uint256 hash = block.GetHash(); + if (block.IsProofOfStake()) { + uint256 hashProofOfStake; + uint256 hash = block.GetHash(); - if (!CheckProofOfStake(block, hashProofOfStake)) { - LogPrintf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); - return false; - } - if (!mapProofOfStake.count(hash)) // add to mapProofOfStake - mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); - } + if (!CheckProofOfStake(block, hashProofOfStake)) { + LogPrintf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); + return false; + } + if (!mapProofOfStake.count(hash)) // add to mapProofOfStake + mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); + } - return true; + return true; } bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex* const pindexPrev) { - uint256 hash = block.GetHash(); + uint256 hash = block.GetHash(); - if (hash == Params().HashGenesisBlock()) - return true; + if (hash == Params().HashGenesisBlock()) + return true; - assert(pindexPrev); + assert(pindexPrev); - int nHeight = pindexPrev->nHeight + 1; + int nHeight = pindexPrev->nHeight + 1; - //If this is a reorg, check that it is not too deep - int nMaxReorgDepth = GetArg("-maxreorg", Params().MaxReorganizationDepth()); - if (chainActive.Height() - nHeight >= nMaxReorgDepth) - return state.DoS(1, error("%s: forked chain older than max reorganization depth (height %d)", __func__, nHeight)); + //If this is a reorg, check that it is not too deep + int nMaxReorgDepth = GetArg("-maxreorg", Params().MaxReorganizationDepth()); + if (chainActive.Height() - nHeight >= nMaxReorgDepth) + return state.DoS(1, error("%s: forked chain older than max reorganization depth (height %d)", __func__, nHeight)); - // Check timestamp against prev - if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { - LogPrintf("Block time = %d , GetMedianTimePast = %d \n", block.GetBlockTime(), pindexPrev->GetMedianTimePast()); - return state.Invalid(error("%s : block's timestamp is too early", __func__), - REJECT_INVALID, "time-too-old"); - } + // Check timestamp against prev + if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { + LogPrintf("Block time = %d , GetMedianTimePast = %d \n", block.GetBlockTime(), pindexPrev->GetMedianTimePast()); + return state.Invalid(error("%s : block's timestamp is too early", __func__), + REJECT_INVALID, "time-too-old"); + } - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(nHeight, hash)) + return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), + REJECT_CHECKPOINT, "checkpoint mismatch"); - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(0, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(0, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); - return true; + return true; } bool IsBlockHashInChain(const uint256& hashBlock) { - if (hashBlock == 0 || !mapBlockIndex.count(hashBlock)) - return false; + if (hashBlock == 0 || !mapBlockIndex.count(hashBlock)) + return false; - return chainActive.Contains(mapBlockIndex[hashBlock]); + return chainActive.Contains(mapBlockIndex[hashBlock]); } bool IsTransactionInChain(uint256 txId, int& nHeightTx) { - uint256 hashBlock; - CTransaction tx; - GetTransaction(txId, tx, hashBlock, true); - if (!IsBlockHashInChain(hashBlock)) - return false; + uint256 hashBlock; + CTransaction tx; + GetTransaction(txId, tx, hashBlock, true); + if (!IsBlockHashInChain(hashBlock)) + return false; - nHeightTx = mapBlockIndex.at(hashBlock)->nHeight; - return true; + nHeightTx = mapBlockIndex.at(hashBlock)->nHeight; + return true; } bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex* const pindexPrev) { - const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { - return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); - } + // Check that all transactions are finalized + BOOST_FOREACH (const CTransaction& tx, block.vtx) + if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { + return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); + } - return true; + return true; } bool AcceptBlockHeader(const CBlock& block, CValidationState& state, CBlockIndex** ppindex) { - AssertLockHeld(cs_main); - // Check for duplicate - uint256 hash = block.GetHash(); - BlockMap::iterator miSelf = mapBlockIndex.find(hash); - CBlockIndex* pindex = NULL; - - // TODO : ENABLE BLOCK CACHE IN SPECIFIC CASES - if (miSelf != mapBlockIndex.end()) { - // Block header is already known. - pindex = miSelf->second; - if (ppindex) - *ppindex = pindex; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate"); - return true; - } - - if (!CheckBlockHeader(block, state, false)) { - LogPrintf("AcceptBlockHeader(): CheckBlockHeader failed \n"); - return false; - } - - // Get prev block index - CBlockIndex* pindexPrev = NULL; - if (hash != Params().HashGenesisBlock()) { - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi == mapBlockIndex.end()) - return state.DoS(0, error("%s : prev block %s not found", __func__, block.hashPrevBlock.ToString().c_str()), 0, "bad-prevblk"); - pindexPrev = (*mi).second; - if (pindexPrev->nStatus & BLOCK_FAILED_MASK) - return state.DoS(100, error("%s : prev block %s is invalid, unable to add block %s", __func__, block.hashPrevBlock.GetHex(), block.GetHash().GetHex()), - REJECT_INVALID, "bad-prevblk"); - } - - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; - - if (pindex == NULL) - pindex = AddToBlockIndex(block); - - if (ppindex) - *ppindex = pindex; - - return true; + AssertLockHeld(cs_main); + // Check for duplicate + uint256 hash = block.GetHash(); + BlockMap::iterator miSelf = mapBlockIndex.find(hash); + CBlockIndex* pindex = NULL; + + // TODO : ENABLE BLOCK CACHE IN SPECIFIC CASES + if (miSelf != mapBlockIndex.end()) { + // Block header is already known. + pindex = miSelf->second; + if (ppindex) + *ppindex = pindex; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate"); + return true; + } + + if (!CheckBlockHeader(block, state, false)) { + LogPrintf("AcceptBlockHeader(): CheckBlockHeader failed \n"); + return false; + } + + // Get prev block index + CBlockIndex* pindexPrev = NULL; + if (hash != Params().HashGenesisBlock()) { + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi == mapBlockIndex.end()) + return state.DoS(0, error("%s : prev block %s not found", __func__, block.hashPrevBlock.ToString().c_str()), 0, "bad-prevblk"); + pindexPrev = (*mi).second; + if (pindexPrev->nStatus & BLOCK_FAILED_MASK) + return state.DoS(100, error("%s : prev block %s is invalid, unable to add block %s", __func__, block.hashPrevBlock.GetHex(), block.GetHash().GetHex()), + REJECT_INVALID, "bad-prevblk"); + } + + if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + return false; + + if (pindex == NULL) + pindex = AddToBlockIndex(block); + + if (ppindex) + *ppindex = pindex; + + return true; } bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp, bool fAlreadyCheckedBlock) { - AssertLockHeld(cs_main); - - CBlockIndex*& pindex = *ppindex; - - // Get prev block index - CBlockIndex* pindexPrev = NULL; - if (block.GetHash() != Params().HashGenesisBlock()) { - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi == mapBlockIndex.end()) - return state.DoS(0, error("%s : prev block %s not found", __func__, block.hashPrevBlock.ToString().c_str()), 0, "bad-prevblk"); - pindexPrev = (*mi).second; - if (pindexPrev->nStatus & BLOCK_FAILED_MASK) - return state.DoS(100, error("%s : prev block %s is invalid, unable to add block %s", __func__, block.hashPrevBlock.GetHex(), block.GetHash().GetHex()), - REJECT_INVALID, "bad-prevblk"); - } - - if (block.GetHash() != Params().HashGenesisBlock() && !CheckWork(block, pindexPrev)) - return false; - - if (!AcceptBlockHeader(block, state, &pindex)) - return false; - - if (pindex->nStatus & BLOCK_HAVE_DATA) { - // TODO: deal better with duplicate blocks. - // return state.DoS(20, error("AcceptBlock() : already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); - return true; - } - - if ((!fAlreadyCheckedBlock && !CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { - if (state.IsInvalid() && !state.CorruptionPossible()) { - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - } - return false; - } - - int nHeight = pindex->nHeight; - - // Write block to history file - try { - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != NULL) - blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize + 8, nHeight, block.GetBlockTime(), dbp != NULL)) - return error("AcceptBlock() : FindBlockPos failed"); - if (dbp == NULL) - if (!WriteBlockToDisk(block, blockPos)) - return state.Abort("Failed to write block"); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) - return error("AcceptBlock() : ReceivedBlockTransactions failed"); - } - catch (std::runtime_error& e) { - return state.Abort(std::string("System error: ") + e.what()); - } - - return true; + AssertLockHeld(cs_main); + + CBlockIndex*& pindex = *ppindex; + + // Get prev block index + CBlockIndex* pindexPrev = NULL; + if (block.GetHash() != Params().HashGenesisBlock()) { + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi == mapBlockIndex.end()) + return state.DoS(0, error("%s : prev block %s not found", __func__, block.hashPrevBlock.ToString().c_str()), 0, "bad-prevblk"); + pindexPrev = (*mi).second; + if (pindexPrev->nStatus & BLOCK_FAILED_MASK) + return state.DoS(100, error("%s : prev block %s is invalid, unable to add block %s", __func__, block.hashPrevBlock.GetHex(), block.GetHash().GetHex()), + REJECT_INVALID, "bad-prevblk"); + } + + if (block.GetHash() != Params().HashGenesisBlock() && !CheckWork(block, pindexPrev)) + return false; + + if (!AcceptBlockHeader(block, state, &pindex)) + return false; + + if (pindex->nStatus & BLOCK_HAVE_DATA) { + // TODO: deal better with duplicate blocks. + // return state.DoS(20, error("AcceptBlock() : already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); + return true; + } + + if ((!fAlreadyCheckedBlock && !CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if (state.IsInvalid() && !state.CorruptionPossible()) { + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + } + return false; + } + + int nHeight = pindex->nHeight; + + // Write block to history file + try { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize + 8, nHeight, block.GetBlockTime(), dbp != NULL)) + return error("AcceptBlock() : FindBlockPos failed"); + if (dbp == NULL) + if (!WriteBlockToDisk(block, blockPos)) + return state.Abort("Failed to write block"); + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + return error("AcceptBlock() : ReceivedBlockTransactions failed"); + } catch (std::runtime_error& e) { + return state.Abort(std::string("System error: ") + e.what()); + } + + return true; } bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired) { - unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority(); - 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); + unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority(); + 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); } /** Turn the lowest '1' bit in the binary representation of a number into a '0'. */ @@ -4754,831 +4596,818 @@ int static inline InvertLowestOne(int n) { return n & (n - 1); } /** Compute what height to jump back to with the CBlockIndex::pskip pointer. */ int static inline GetSkipHeight(int height) { - if (height < 2) - return 0; + if (height < 2) + return 0; - // Determine which height to jump back to. Any number strictly lower than height is acceptable, - // but the following expression seems to perform well in simulations (max 110 steps to go back - // up to 2**18 blocks). - return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height); + // Determine which height to jump back to. Any number strictly lower than height is acceptable, + // but the following expression seems to perform well in simulations (max 110 steps to go back + // up to 2**18 blocks). + return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height); } CBlockIndex* CBlockIndex::GetAncestor(int height) { - if (height > nHeight || height < 0) - return NULL; - - CBlockIndex* pindexWalk = this; - int heightWalk = nHeight; - while (heightWalk > height) { - int heightSkip = GetSkipHeight(heightWalk); - int heightSkipPrev = GetSkipHeight(heightWalk - 1); - if (heightSkip == height || - (heightSkip > height && !(heightSkipPrev < heightSkip - 2 && heightSkipPrev >= height))) { - // Only follow pskip if pprev->pskip isn't better than pskip->pprev. - pindexWalk = pindexWalk->pskip; - heightWalk = heightSkip; - } - else { - pindexWalk = pindexWalk->pprev; - heightWalk--; - } - } - return pindexWalk; + if (height > nHeight || height < 0) + return NULL; + + CBlockIndex* pindexWalk = this; + int heightWalk = nHeight; + while (heightWalk > height) { + int heightSkip = GetSkipHeight(heightWalk); + int heightSkipPrev = GetSkipHeight(heightWalk - 1); + if (heightSkip == height || + (heightSkip > height && !(heightSkipPrev < heightSkip - 2 && heightSkipPrev >= height))) { + // Only follow pskip if pprev->pskip isn't better than pskip->pprev. + pindexWalk = pindexWalk->pskip; + heightWalk = heightSkip; + } else { + pindexWalk = pindexWalk->pprev; + heightWalk--; + } + } + return pindexWalk; } const CBlockIndex* CBlockIndex::GetAncestor(int height) const { - return const_cast(this)->GetAncestor(height); + return const_cast(this)->GetAncestor(height); } void CBlockIndex::BuildSkip() { - if (pprev) - pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); + if (pprev) + pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); } bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDiskBlockPos* dbp) { - // Preliminary checks - int64_t nStartTime = GetTimeMillis(); - bool checked = CheckBlock(*pblock, state); - - int nMints = 0; - int nSpends = 0; - for (const CTransaction tx : pblock->vtx) { - if (tx.ContainsZerocoins()) { - for (const CTxIn in : tx.vin) { - if (in.scriptSig.IsZerocoinSpend()) - nSpends++; - } - for (const CTxOut out : tx.vout) { - if (out.IsZerocoinMint()) - nMints++; - } - } - } - if (nMints || nSpends) - LogPrintf("%s : block contains %d zCcbc mints and %d zCcbc spends\n", __func__, nMints, nSpends); - - // ppcoin: check proof-of-stake - // Limited duplicity on stake: prevents block flood attack - // Duplicate stake allowed only when there is orphan child block - //if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake())/* && !mapOrphanBlocksByPrev.count(hash)*/) - // return error("ProcessNewBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, pblock->GetHash().ToString().c_str()); - - // NovaCoin: check proof-of-stake block signature - if (!pblock->CheckBlockSignature()) - return error("ProcessNewBlock() : bad proof-of-stake block signature"); - - if (pblock->GetHash() != Params().HashGenesisBlock() && pfrom != NULL) { - //if we get this far, check if the prev block is our prev block, if not then request sync and return false - BlockMap::iterator mi = mapBlockIndex.find(pblock->hashPrevBlock); - if (mi == mapBlockIndex.end()) { - pfrom->PushMessage("getblocks", chainActive.GetLocator(), uint256(0)); - return false; - } - } - - { - LOCK(cs_main); // Replaces the former TRY_LOCK loop because busy waiting wastes too much resources - - MarkBlockAsReceived(pblock->GetHash()); - if (!checked) { - return error("%s : CheckBlock FAILED for block %s", __func__, pblock->GetHash().GetHex()); - } - - // Store to disk - CBlockIndex* pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, dbp, checked); - if (pindex && pfrom) { - mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); - } - CheckBlockIndex(); - if (!ret) - return error("%s : AcceptBlock FAILED", __func__); - } - - if (!ActivateBestChain(state, pblock, checked)) - return error("%s : ActivateBestChain failed", __func__); - - if (!fLiteMode) { - if (masternodeSync.RequestedMasternodeAssets > MASTERNODE_SYNC_LIST) { - obfuScationPool.NewBlock(); - masternodePayments.ProcessBlock(GetHeight() + 10); - budget.NewBlock(); - } - } - - if (pwalletMain) { - // If turned on MultiSend will send a transaction (or more) on the after maturity of a stake - if (pwalletMain->isMultiSendEnabled()) - pwalletMain->MultiSend(); - - // If turned on Auto Combine will scan wallet for dust to combine - if (pwalletMain->fCombineDust) - pwalletMain->AutoCombineDust(); - } - - LogPrintf("%s : ACCEPTED in %ld milliseconds with size=%d\n", __func__, GetTimeMillis() - nStartTime, - pblock->GetSerializeSize(SER_DISK, CLIENT_VERSION)); - - return true; + // Preliminary checks + int64_t nStartTime = GetTimeMillis(); + bool checked = CheckBlock(*pblock, state); + + int nMints = 0; + int nSpends = 0; + for (const CTransaction tx : pblock->vtx) { + if (tx.ContainsZerocoins()) { + for (const CTxIn in : tx.vin) { + if (in.scriptSig.IsZerocoinSpend()) + nSpends++; + } + for (const CTxOut out : tx.vout) { + if (out.IsZerocoinMint()) + nMints++; + } + } + } + if (nMints || nSpends) + LogPrintf("%s : block contains %d zCcbc mints and %d zCcbc spends\n", __func__, nMints, nSpends); + + // ppcoin: check proof-of-stake + // Limited duplicity on stake: prevents block flood attack + // Duplicate stake allowed only when there is orphan child block + //if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake())/* && !mapOrphanBlocksByPrev.count(hash)*/) + // return error("ProcessNewBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, pblock->GetHash().ToString().c_str()); + + // NovaCoin: check proof-of-stake block signature + if (!pblock->CheckBlockSignature()) + return error("ProcessNewBlock() : bad proof-of-stake block signature"); + + if (pblock->GetHash() != Params().HashGenesisBlock() && pfrom != NULL) { + //if we get this far, check if the prev block is our prev block, if not then request sync and return false + BlockMap::iterator mi = mapBlockIndex.find(pblock->hashPrevBlock); + if (mi == mapBlockIndex.end()) { + pfrom->PushMessage("getblocks", chainActive.GetLocator(), uint256(0)); + return false; + } + } + + { + LOCK(cs_main); // Replaces the former TRY_LOCK loop because busy waiting wastes too much resources + + MarkBlockAsReceived(pblock->GetHash()); + if (!checked) { + return error("%s : CheckBlock FAILED for block %s", __func__, pblock->GetHash().GetHex()); + } + + // Store to disk + CBlockIndex* pindex = NULL; + bool ret = AcceptBlock(*pblock, state, &pindex, dbp, checked); + if (pindex && pfrom) { + mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); + } + CheckBlockIndex(); + if (!ret) + return error("%s : AcceptBlock FAILED", __func__); + } + + if (!ActivateBestChain(state, pblock, checked)) + return error("%s : ActivateBestChain failed", __func__); + + if (!fLiteMode) { + if (masternodeSync.RequestedMasternodeAssets > MASTERNODE_SYNC_LIST) { + obfuScationPool.NewBlock(); + masternodePayments.ProcessBlock(GetHeight() + 10); + budget.NewBlock(); + } + } + + if (pwalletMain) { + // If turned on MultiSend will send a transaction (or more) on the after maturity of a stake + if (pwalletMain->isMultiSendEnabled()) + pwalletMain->MultiSend(); + + // If turned on Auto Combine will scan wallet for dust to combine + if (pwalletMain->fCombineDust) + pwalletMain->AutoCombineDust(); + } + + LogPrintf("%s : ACCEPTED in %ld milliseconds with size=%d\n", __func__, GetTimeMillis() - nStartTime, + pblock->GetSerializeSize(SER_DISK, CLIENT_VERSION)); + + return true; } bool TestBlockValidity(CValidationState& state, const CBlock& block, CBlockIndex* const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { - AssertLockHeld(cs_main); - assert(pindexPrev == chainActive.Tip()); - - CCoinsViewCache viewNew(pcoinsTip); - CBlockIndex indexDummy(block); - indexDummy.pprev = pindexPrev; - indexDummy.nHeight = pindexPrev->nHeight + 1; - - // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; - if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) - return false; - if (!ContextualCheckBlock(block, state, pindexPrev)) - return false; - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) - return false; - assert(state.IsValid()); + AssertLockHeld(cs_main); + assert(pindexPrev == chainActive.Tip()); - return true; + CCoinsViewCache viewNew(pcoinsTip); + CBlockIndex indexDummy(block); + indexDummy.pprev = pindexPrev; + indexDummy.nHeight = pindexPrev->nHeight + 1; + + // NOTE: CheckBlockHeader is called by CheckBlock + if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + return false; + if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) + return false; + if (!ContextualCheckBlock(block, state, pindexPrev)) + return false; + if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + return false; + assert(state.IsValid()); + + return true; } bool AbortNode(const std::string& strMessage, const std::string& userMessage) { - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; + strMiscWarning = strMessage; + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; } bool CheckDiskSpace(uint64_t nAdditionalBytes) { - uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available; - // Check for nMinDiskSpace bytes (currently 50MB) - if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) - return AbortNode("Disk space is low!", _("Error: Disk space is low!")); + // Check for nMinDiskSpace bytes (currently 50MB) + if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) + return AbortNode("Disk space is low!", _("Error: Disk space is low!")); - return true; + return true; } FILE* OpenDiskFile(const CDiskBlockPos& pos, const char* prefix, bool fReadOnly) { - if (pos.IsNull()) - return NULL; - boost::filesystem::path path = GetBlockPosFilename(pos, prefix); - 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) { - LogPrintf("Unable to open file %s\n", path.string()); - return NULL; - } - if (pos.nPos) { - if (fseek(file, pos.nPos, SEEK_SET)) { - LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); - fclose(file); - return NULL; - } - } - return file; + if (pos.IsNull()) + return NULL; + boost::filesystem::path path = GetBlockPosFilename(pos, prefix); + 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) { + LogPrintf("Unable to open file %s\n", path.string()); + return NULL; + } + if (pos.nPos) { + if (fseek(file, pos.nPos, SEEK_SET)) { + LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); + fclose(file); + return NULL; + } + } + return file; } FILE* OpenBlockFile(const CDiskBlockPos& pos, bool fReadOnly) { - return OpenDiskFile(pos, "blk", fReadOnly); + return OpenDiskFile(pos, "blk", fReadOnly); } FILE* OpenUndoFile(const CDiskBlockPos& pos, bool fReadOnly) { - return OpenDiskFile(pos, "rev", fReadOnly); + return OpenDiskFile(pos, "rev", fReadOnly); } boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos& pos, const char* prefix) { - return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); + return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); } CBlockIndex* InsertBlockIndex(uint256 hash) { - if (hash == 0) - return NULL; + if (hash == 0) + return NULL; - // Return existing - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - return (*mi).second; + // Return existing + BlockMap::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; + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - //mark as PoS seen - if (pindexNew->IsProofOfStake()) - setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); + //mark as PoS seen + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); - pindexNew->phashBlock = &((*mi).first); + pindexNew->phashBlock = &((*mi).first); - return pindexNew; + return pindexNew; } bool static LoadBlockIndexDB() { - 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) + GetBlockProof(*pindex); - if (pindex->nStatus & BLOCK_HAVE_DATA) { - if (pindex->pprev) { - if (pindex->pprev->nChainTx) { - pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; - } - else { - pindex->nChainTx = 0; - mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); - } - } - else { - pindex->nChainTx = pindex->nTx; - } - } - if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) - setBlockIndexCandidates.insert(pindex); - if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) - pindexBestInvalid = pindex; - if (pindex->pprev) - pindex->BuildSkip(); - if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) - pindexBestHeader = pindex; - } - - // Load block file info - pblocktree->ReadLastBlockFile(nLastBlockFile); - vinfoBlockFile.resize(nLastBlockFile + 1); - LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); - for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { - pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); - } - LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); - for (int nFile = nLastBlockFile + 1; true; nFile++) { - CBlockFileInfo info; - if (pblocktree->ReadBlockFileInfo(nFile, info)) { - vinfoBlockFile.push_back(info); - } - else { - break; - } - } - - // Check presence of blk files - LogPrintf("Checking all blk files are present...\n"); - set setBlkDataFiles; - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*) & item, mapBlockIndex) { - CBlockIndex* pindex = item.second; - if (pindex->nStatus & BLOCK_HAVE_DATA) { - setBlkDataFiles.insert(pindex->nFile); - } - } - for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) { - CDiskBlockPos pos(*it, 0); - if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) { - return false; - } - } - - //Check if the shutdown procedure was followed on last client exit - bool fLastShutdownWasPrepared = true; - pblocktree->ReadFlag("shutdown", fLastShutdownWasPrepared); - LogPrintf("%s: Last shutdown was prepared: %s\n", __func__, fLastShutdownWasPrepared); - - //Check for inconsistency with block file info and internal state - if (!fLastShutdownWasPrepared && !GetBoolArg("-forcestart", false) && !GetBoolArg("-reindex", false) && (vSortedByHeight.size() != vinfoBlockFile[nLastBlockFile].nHeightLast + 1) && (vinfoBlockFile[nLastBlockFile].nHeightLast != 0)) { - //The database is in a state where a block has been accepted and written to disk, but not - //all of the block has perculated through the code. The block and the index should both be - //intact (although assertions are added if they are not), and the block will be reprocessed - //to ensure all data will be accounted for. - LogPrintf("%s: Inconsistent State Detected mapBlockIndex.size()=%d blockFileBlocks=%d\n", __func__, vSortedByHeight.size(), vinfoBlockFile[nLastBlockFile].nHeightLast + 1); - LogPrintf("%s: lastIndexPos=%d blockFileSize=%d\n", __func__, vSortedByHeight[vSortedByHeight.size() - 1].second->GetBlockPos().nPos, - vinfoBlockFile[nLastBlockFile].nSize); - - //try reading the block from the last index we have - bool isFixed = true; - string strError = ""; - LogPrintf("%s: Attempting to re-add last block that was recorded to disk\n", __func__); - - //get the last block that was properly recorded to the block info file - CBlockIndex* pindexLastMeta = vSortedByHeight[vinfoBlockFile[nLastBlockFile].nHeightLast + 1].second; - - //fix Assertion `hashPrevBlock == view.GetBestBlock()' failed. By adjusting height to the last recorded by coinsview - CBlockIndex* pindexCoinsView = mapBlockIndex[pcoinsTip->GetBestBlock()]; - for (unsigned int i = vinfoBlockFile[nLastBlockFile].nHeightLast + 1; i < vSortedByHeight.size(); i++) { - pindexLastMeta = vSortedByHeight[i].second; - if (pindexLastMeta->nHeight > pindexCoinsView->nHeight) - break; - } - - LogPrintf("%s: Last block properly recorded: #%d %s\n", __func__, pindexLastMeta->nHeight, pindexLastMeta->GetBlockHash().ToString().c_str()); - - CBlock lastMetaBlock; - if (!ReadBlockFromDisk(lastMetaBlock, pindexLastMeta)) { - isFixed = false; - strError = strprintf("failed to read block %d from disk", pindexLastMeta->nHeight); - } - - //set the chain to the block before lastMeta so that the meta block will be seen as new - chainActive.SetTip(pindexLastMeta->pprev); - - //Process the lastMetaBlock again, using the known location on disk - CDiskBlockPos blockPos = pindexLastMeta->GetBlockPos(); - CValidationState state; - ProcessNewBlock(state, NULL, &lastMetaBlock, &blockPos); - - //ensure that everything is as it should be - if (pcoinsTip->GetBestBlock() != vSortedByHeight[vSortedByHeight.size() - 1].second->GetBlockHash()) { - isFixed = false; - strError = "pcoinsTip best block is not correct"; - } - - //properly account for all of the blocks that were not in the meta data. If this is not done the file - //positioning will be wrong and blocks will be overwritten and later cause serialization errors - CBlockIndex* pindexLast = vSortedByHeight[vSortedByHeight.size() - 1].second; - CBlock lastBlock; - if (!ReadBlockFromDisk(lastBlock, pindexLast)) { - isFixed = false; - strError = strprintf("failed to read block %d from disk", pindexLast->nHeight); - } - vinfoBlockFile[nLastBlockFile].nHeightLast = pindexLast->nHeight; - vinfoBlockFile[nLastBlockFile].nSize = pindexLast->GetBlockPos().nPos + ::GetSerializeSize(lastBlock, SER_DISK, CLIENT_VERSION); - ; - setDirtyFileInfo.insert(nLastBlockFile); - FlushStateToDisk(state, FLUSH_STATE_ALWAYS); - - //Print out file info again - pblocktree->ReadLastBlockFile(nLastBlockFile); - vinfoBlockFile.resize(nLastBlockFile + 1); - LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); - for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { - pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); - } - LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); - - if (!isFixed) { - strError = "Failed reading from database. " + strError + ". The block database is in an inconsistent state and may cause issues in the future." - "To force start use -forcestart"; - uiInterface.ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); - abort(); - } - LogPrintf("Passed corruption fix\n"); - } - - // 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); - LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); - - // If this is written true before the next client init, then we know the shutdown process failed - pblocktree->WriteFlag("shutdown", false); - - // Load pointer to end of best chain - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - if (it == mapBlockIndex.end()) - return true; - chainActive.SetTip(it->second); - - PruneBlockIndexCandidates(); - - LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s progress=%f\n", - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainActive.Tip())); - - return true; + 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) + GetBlockProof(*pindex); + if (pindex->nStatus & BLOCK_HAVE_DATA) { + if (pindex->pprev) { + if (pindex->pprev->nChainTx) { + pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; + } else { + pindex->nChainTx = 0; + mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); + } + } else { + pindex->nChainTx = pindex->nTx; + } + } + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) + setBlockIndexCandidates.insert(pindex); + if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) + pindexBestInvalid = pindex; + if (pindex->pprev) + pindex->BuildSkip(); + if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) + pindexBestHeader = pindex; + } + + // Load block file info + pblocktree->ReadLastBlockFile(nLastBlockFile); + vinfoBlockFile.resize(nLastBlockFile + 1); + LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); + for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { + pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); + } + LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); + for (int nFile = nLastBlockFile + 1; true; nFile++) { + CBlockFileInfo info; + if (pblocktree->ReadBlockFileInfo(nFile, info)) { + vinfoBlockFile.push_back(info); + } else { + break; + } + } + + // Check presence of blk files + LogPrintf("Checking all blk files are present...\n"); + set setBlkDataFiles; + BOOST_FOREACH (const PAIRTYPE(uint256, CBlockIndex*) & item, mapBlockIndex) { + CBlockIndex* pindex = item.second; + if (pindex->nStatus & BLOCK_HAVE_DATA) { + setBlkDataFiles.insert(pindex->nFile); + } + } + for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) { + CDiskBlockPos pos(*it, 0); + if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) { + return false; + } + } + + //Check if the shutdown procedure was followed on last client exit + bool fLastShutdownWasPrepared = true; + pblocktree->ReadFlag("shutdown", fLastShutdownWasPrepared); + LogPrintf("%s: Last shutdown was prepared: %s\n", __func__, fLastShutdownWasPrepared); + + //Check for inconsistency with block file info and internal state + if (!fLastShutdownWasPrepared && !GetBoolArg("-forcestart", false) && !GetBoolArg("-reindex", false) && (vSortedByHeight.size() != vinfoBlockFile[nLastBlockFile].nHeightLast + 1) && (vinfoBlockFile[nLastBlockFile].nHeightLast != 0)) { + //The database is in a state where a block has been accepted and written to disk, but not + //all of the block has perculated through the code. The block and the index should both be + //intact (although assertions are added if they are not), and the block will be reprocessed + //to ensure all data will be accounted for. + LogPrintf("%s: Inconsistent State Detected mapBlockIndex.size()=%d blockFileBlocks=%d\n", __func__, vSortedByHeight.size(), vinfoBlockFile[nLastBlockFile].nHeightLast + 1); + LogPrintf("%s: lastIndexPos=%d blockFileSize=%d\n", __func__, vSortedByHeight[vSortedByHeight.size() - 1].second->GetBlockPos().nPos, + vinfoBlockFile[nLastBlockFile].nSize); + + //try reading the block from the last index we have + bool isFixed = true; + string strError = ""; + LogPrintf("%s: Attempting to re-add last block that was recorded to disk\n", __func__); + + //get the last block that was properly recorded to the block info file + CBlockIndex* pindexLastMeta = vSortedByHeight[vinfoBlockFile[nLastBlockFile].nHeightLast + 1].second; + + //fix Assertion `hashPrevBlock == view.GetBestBlock()' failed. By adjusting height to the last recorded by coinsview + CBlockIndex* pindexCoinsView = mapBlockIndex[pcoinsTip->GetBestBlock()]; + for (unsigned int i = vinfoBlockFile[nLastBlockFile].nHeightLast + 1; i < vSortedByHeight.size(); i++) { + pindexLastMeta = vSortedByHeight[i].second; + if (pindexLastMeta->nHeight > pindexCoinsView->nHeight) + break; + } + + LogPrintf("%s: Last block properly recorded: #%d %s\n", __func__, pindexLastMeta->nHeight, pindexLastMeta->GetBlockHash().ToString().c_str()); + + CBlock lastMetaBlock; + if (!ReadBlockFromDisk(lastMetaBlock, pindexLastMeta)) { + isFixed = false; + strError = strprintf("failed to read block %d from disk", pindexLastMeta->nHeight); + } + + //set the chain to the block before lastMeta so that the meta block will be seen as new + chainActive.SetTip(pindexLastMeta->pprev); + + //Process the lastMetaBlock again, using the known location on disk + CDiskBlockPos blockPos = pindexLastMeta->GetBlockPos(); + CValidationState state; + ProcessNewBlock(state, NULL, &lastMetaBlock, &blockPos); + + //ensure that everything is as it should be + if (pcoinsTip->GetBestBlock() != vSortedByHeight[vSortedByHeight.size() - 1].second->GetBlockHash()) { + isFixed = false; + strError = "pcoinsTip best block is not correct"; + } + + //properly account for all of the blocks that were not in the meta data. If this is not done the file + //positioning will be wrong and blocks will be overwritten and later cause serialization errors + CBlockIndex* pindexLast = vSortedByHeight[vSortedByHeight.size() - 1].second; + CBlock lastBlock; + if (!ReadBlockFromDisk(lastBlock, pindexLast)) { + isFixed = false; + strError = strprintf("failed to read block %d from disk", pindexLast->nHeight); + } + vinfoBlockFile[nLastBlockFile].nHeightLast = pindexLast->nHeight; + vinfoBlockFile[nLastBlockFile].nSize = pindexLast->GetBlockPos().nPos + ::GetSerializeSize(lastBlock, SER_DISK, CLIENT_VERSION); + ; + setDirtyFileInfo.insert(nLastBlockFile); + FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + + //Print out file info again + pblocktree->ReadLastBlockFile(nLastBlockFile); + vinfoBlockFile.resize(nLastBlockFile + 1); + LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); + for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { + pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); + } + LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); + + if (!isFixed) { + strError = "Failed reading from database. " + strError + ". The block database is in an inconsistent state and may cause issues in the future." + "To force start use -forcestart"; + uiInterface.ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); + abort(); + } + LogPrintf("Passed corruption fix\n"); + } + + // 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); + LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + + // If this is written true before the next client init, then we know the shutdown process failed + pblocktree->WriteFlag("shutdown", false); + + // Load pointer to end of best chain + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + if (it == mapBlockIndex.end()) + return true; + chainActive.SetTip(it->second); + + PruneBlockIndexCandidates(); + + LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s progress=%f\n", + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainActive.Tip())); + + return true; } CVerifyDB::CVerifyDB() { - uiInterface.ShowProgress(_("Verifying blocks..."), 0); + uiInterface.ShowProgress(_("Verifying blocks..."), 0); } CVerifyDB::~CVerifyDB() { - uiInterface.ShowProgress("", 100); + uiInterface.ShowProgress("", 100); } bool CVerifyDB::VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth) { - LOCK(cs_main); - if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) - return true; - - // Verify blocks in the best chain - if (nCheckDepth <= 0) - nCheckDepth = 1000000000; // suffices until the year 19000 - if (nCheckDepth > chainActive.Height()) - nCheckDepth = chainActive.Height(); - nCheckLevel = std::max(0, std::min(4, nCheckLevel)); - LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CCoinsViewCache coins(coinsview); - CBlockIndex* pindexState = chainActive.Tip(); - CBlockIndex* pindexFailure = NULL; - int nGoodTransactions = 0; - CValidationState state; - for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { - boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); - if (pindex->nHeight < chainActive.Height() - nCheckDepth) - break; - CBlock block; - // check level 0: read from disk - if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(block, state)) - return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); - // 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()); - } - } - // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) { - bool fClean = true; - if (!DisconnectBlock(block, state, pindex, coins, &fClean)) - return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - pindexState = pindex->pprev; - if (!fClean) { - nGoodTransactions = 0; - pindexFailure = pindex; - } - else - nGoodTransactions += block.vtx.size(); - } - if (ShutdownRequested()) - return true; - } - if (pindexFailure) - return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); - - // check level 4: try reconnecting blocks - if (nCheckLevel >= 4) { - CBlockIndex* pindex = pindexState; - while (pindex != chainActive.Tip()) { - boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); - pindex = chainActive.Next(pindex); - CBlock block; - if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins, false)) - return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - } - } - - LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); - - return true; + LOCK(cs_main); + if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) + return true; + + // Verify blocks in the best chain + if (nCheckDepth <= 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > chainActive.Height()) + nCheckDepth = chainActive.Height(); + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); + LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CCoinsViewCache coins(coinsview); + CBlockIndex* pindexState = chainActive.Tip(); + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; + CValidationState state; + for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { + boost::this_thread::interruption_point(); + uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); + if (pindex->nHeight < chainActive.Height() - nCheckDepth) + break; + CBlock block; + // check level 0: read from disk + if (!ReadBlockFromDisk(block, pindex)) + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + // check level 1: verify block validity + if (nCheckLevel >= 1 && !CheckBlock(block, state)) + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + // 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()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) { + bool fClean = true; + if (!DisconnectBlock(block, state, pindex, coins, &fClean)) + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); + } + if (ShutdownRequested()) + return true; + } + if (pindexFailure) + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex* pindex = pindexState; + while (pindex != chainActive.Tip()) { + boost::this_thread::interruption_point(); + uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); + pindex = chainActive.Next(pindex); + CBlock block; + if (!ReadBlockFromDisk(block, pindex)) + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + if (!ConnectBlock(block, state, pindex, coins, false)) + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + } + } + + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); + + return true; } void UnloadBlockIndex() { - mapBlockIndex.clear(); - setBlockIndexCandidates.clear(); - chainActive.SetTip(NULL); - pindexBestInvalid = NULL; + mapBlockIndex.clear(); + setBlockIndexCandidates.clear(); + chainActive.SetTip(NULL); + pindexBestInvalid = NULL; } bool LoadBlockIndex() { - // Load block index from databases - if (!fReindex && !LoadBlockIndexDB()) - return false; - return true; + // Load block index from databases + if (!fReindex && !LoadBlockIndexDB()) + return false; + return true; } bool InitBlockIndex() { - LOCK(cs_main); - // Check whether we're already initialized - if (chainActive.Genesis() != NULL) - return true; - - // Use the provided setting for -txindex in the new database - fTxIndex = GetBoolArg("-txindex", true); - pblocktree->WriteFlag("txindex", fTxIndex); - LogPrintf("Initializing databases...\n"); - - // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) - if (!fReindex) { - try { - CBlock& block = const_cast(Params().GenesisBlock()); - // Start new block file - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - CValidationState state; - if (!FindBlockPos(state, blockPos, nBlockSize + 8, 0, block.GetBlockTime())) - return error("LoadBlockIndex() : FindBlockPos failed"); - if (!WriteBlockToDisk(block, blockPos)) - return error("LoadBlockIndex() : writing genesis block to disk failed"); - CBlockIndex* pindex = AddToBlockIndex(block); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) - return error("LoadBlockIndex() : genesis block not accepted"); - if (!ActivateBestChain(state, &block)) - return error("LoadBlockIndex() : genesis block cannot be activated"); - // Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data - return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); - } - catch (std::runtime_error& e) { - return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); - } - } - - return true; + LOCK(cs_main); + // Check whether we're already initialized + if (chainActive.Genesis() != NULL) + return true; + + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", true); + pblocktree->WriteFlag("txindex", fTxIndex); + LogPrintf("Initializing databases...\n"); + + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) + if (!fReindex) { + try { + CBlock& block = const_cast(Params().GenesisBlock()); + // Start new block file + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize + 8, 0, block.GetBlockTime())) + return error("LoadBlockIndex() : FindBlockPos failed"); + if (!WriteBlockToDisk(block, blockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + CBlockIndex* pindex = AddToBlockIndex(block); + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + if (!ActivateBestChain(state, &block)) + return error("LoadBlockIndex() : genesis block cannot be activated"); + // Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data + return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + } catch (std::runtime_error& e) { + return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); + } + } + + return true; } bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos* dbp) { - // Map of disk positions for blocks with unknown parent (only used for reindex) - static std::multimap mapBlocksUnknownParent; - int64_t nStart = GetTimeMillis(); - - int nLoaded = 0; - try { - // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2 * MAX_BLOCK_SIZE_CURRENT, MAX_BLOCK_SIZE_CURRENT + 8, SER_DISK, CLIENT_VERSION); - uint64_t nRewind = blkdat.GetPos(); - while (!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[MESSAGE_START_SIZE]; - blkdat.FindByte(Params().MessageStart()[0]); - nRewind = blkdat.GetPos() + 1; - blkdat >> FLATDATA(buf); - if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE)) - continue; - // read size - blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE_CURRENT) - continue; - } - catch (const std::exception&) { - // no valid block header found; don't complain - break; - } - try { - // read block - uint64_t nBlockPos = blkdat.GetPos(); - if (dbp) - dbp->nPos = nBlockPos; - blkdat.SetLimit(nBlockPos + nSize); - blkdat.SetPos(nBlockPos); - CBlock block; - blkdat >> block; - nRewind = blkdat.GetPos(); - - // detect out of order blocks, and store them for later - uint256 hash = block.GetHash(); - if (hash != Params().HashGenesisBlock() && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { - LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), - block.hashPrevBlock.ToString()); - if (dbp) - mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); - continue; - } - - // process in case the block isn't known yet - if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { - CValidationState state; - if (ProcessNewBlock(state, NULL, &block, dbp)) - nLoaded++; - if (state.IsError()) - break; - } - else if (hash != Params().HashGenesisBlock() && mapBlockIndex[hash]->nHeight % 1000 == 0) { - LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); - } - - // Recursively process earlier encountered successors of this block - deque queue; - queue.push_back(hash); - while (!queue.empty()) { - uint256 head = queue.front(); - queue.pop_front(); - std::pair::iterator, std::multimap::iterator> range = mapBlocksUnknownParent.equal_range(head); - while (range.first != range.second) { - std::multimap::iterator it = range.first; - if (ReadBlockFromDisk(block, it->second)) { - LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), - head.ToString()); - CValidationState dummy; - if (ProcessNewBlock(dummy, NULL, &block, &it->second)) { - nLoaded++; - queue.push_back(block.GetHash()); - } - } - range.first++; - mapBlocksUnknownParent.erase(it); - } - } - } - catch (std::exception& e) { - LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); - } - } - } - catch (std::runtime_error& e) { - AbortNode(std::string("System error: ") + e.what()); - } - if (nLoaded > 0) - LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); - return nLoaded > 0; + // Map of disk positions for blocks with unknown parent (only used for reindex) + static std::multimap mapBlocksUnknownParent; + int64_t nStart = GetTimeMillis(); + + int nLoaded = 0; + try { + // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor + CBufferedFile blkdat(fileIn, 2 * MAX_BLOCK_SIZE_CURRENT, MAX_BLOCK_SIZE_CURRENT + 8, SER_DISK, CLIENT_VERSION); + uint64_t nRewind = blkdat.GetPos(); + while (!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[MESSAGE_START_SIZE]; + blkdat.FindByte(Params().MessageStart()[0]); + nRewind = blkdat.GetPos() + 1; + blkdat >> FLATDATA(buf); + if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE)) + continue; + // read size + blkdat >> nSize; + if (nSize < 80 || nSize > MAX_BLOCK_SIZE_CURRENT) + continue; + } catch (const std::exception&) { + // no valid block header found; don't complain + break; + } + try { + // read block + uint64_t nBlockPos = blkdat.GetPos(); + if (dbp) + dbp->nPos = nBlockPos; + blkdat.SetLimit(nBlockPos + nSize); + blkdat.SetPos(nBlockPos); + CBlock block; + blkdat >> block; + nRewind = blkdat.GetPos(); + + // detect out of order blocks, and store them for later + uint256 hash = block.GetHash(); + if (hash != Params().HashGenesisBlock() && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { + LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), + block.hashPrevBlock.ToString()); + if (dbp) + mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); + continue; + } + + // process in case the block isn't known yet + if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { + CValidationState state; + if (ProcessNewBlock(state, NULL, &block, dbp)) + nLoaded++; + if (state.IsError()) + break; + } else if (hash != Params().HashGenesisBlock() && mapBlockIndex[hash]->nHeight % 1000 == 0) { + LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); + } + + // Recursively process earlier encountered successors of this block + deque queue; + queue.push_back(hash); + while (!queue.empty()) { + uint256 head = queue.front(); + queue.pop_front(); + std::pair::iterator, std::multimap::iterator> range = mapBlocksUnknownParent.equal_range(head); + while (range.first != range.second) { + std::multimap::iterator it = range.first; + if (ReadBlockFromDisk(block, it->second)) { + LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + head.ToString()); + CValidationState dummy; + if (ProcessNewBlock(dummy, NULL, &block, &it->second)) { + nLoaded++; + queue.push_back(block.GetHash()); + } + } + range.first++; + mapBlocksUnknownParent.erase(it); + } + } + } catch (std::exception& e) { + LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + } + } catch (std::runtime_error& e) { + AbortNode(std::string("System error: ") + e.what()); + } + if (nLoaded > 0) + LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); + return nLoaded > 0; } void static CheckBlockIndex() { - if (!fCheckBlockIndex) { - return; - } - - LOCK(cs_main); - - // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, - // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when - // iterating the block tree require that chainActive has been initialized.) - if (chainActive.Height() < 0) { - assert(mapBlockIndex.size() <= 1); - return; - } - - // Build forward-pointing map of the entire block tree. - std::multimap forward; - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - forward.insert(std::make_pair(it->second->pprev, it->second)); - } - - assert(forward.size() == mapBlockIndex.size()); - - std::pair::iterator, std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); - CBlockIndex* pindex = rangeGenesis.first->second; - rangeGenesis.first++; - assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. - - // Iterate over the entire block tree, using depth-first search. - // Along the way, remember whether there are blocks on the path from genesis - // block being explored which are the first to have certain properties. - size_t nNodes = 0; - int nHeight = 0; - CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. - CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. - CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). - CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). - CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). - while (pindex != NULL) { - nNodes++; - if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; - if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; - if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; - - // Begin: actual consistency checks. - if (pindex->pprev == NULL) { - // Genesis block checks. - assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match. - assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. - } - // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) - assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); - assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); - if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked - // All parents having data is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. - assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. - assert(pindex->nHeight == nHeight); // nHeight must be consistent. - assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. - assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. - assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid - if (pindexFirstInvalid == NULL) { - // Checks for not-invalid blocks. - assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. - } - if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) { - if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates. - assert(setBlockIndexCandidates.count(pindex)); - } - } - else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates. - assert(setBlockIndexCandidates.count(pindex) == 0); - } - // Check whether this block is in mapBlocksUnlinked. - std::pair::iterator, std::multimap::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); - bool foundInUnlinked = false; - while (rangeUnlinked.first != rangeUnlinked.second) { - assert(rangeUnlinked.first->first == pindex->pprev); - if (rangeUnlinked.first->second == pindex) { - foundInUnlinked = true; - break; - } - rangeUnlinked.first++; - } - if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) { - if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked. - assert(foundInUnlinked); - } - } - else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked. - assert(!foundInUnlinked); - } - // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow - // End: actual consistency checks. - - // Try descending into the first subnode. - std::pair::iterator, std::multimap::iterator> range = forward.equal_range(pindex); - if (range.first != range.second) { - // A subnode was found. - pindex = range.first->second; - nHeight++; - continue; - } - // This is a leaf node. - // Move upwards until we reach a node of which we have not yet visited the last child. - while (pindex) { - // We are going to either move to a parent or a sibling of pindex. - // If pindex was the first with a certain property, unset the corresponding variable. - if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; - if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; - if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; - if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; - if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; - // Find our parent. - CBlockIndex* pindexPar = pindex->pprev; - // Find which child we just visited. - std::pair::iterator, std::multimap::iterator> rangePar = forward.equal_range(pindexPar); - while (rangePar.first->second != pindex) { - assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child. - rangePar.first++; - } - // Proceed to the next one. - rangePar.first++; - if (rangePar.first != rangePar.second) { - // Move to the sibling. - pindex = rangePar.first->second; - break; - } - else { - // Move up further. - pindex = pindexPar; - nHeight--; - continue; - } - } - } - - // Check that we actually traversed the entire map. - assert(nNodes == forward.size()); + if (!fCheckBlockIndex) { + return; + } + + LOCK(cs_main); + + // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, + // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when + // iterating the block tree require that chainActive has been initialized.) + if (chainActive.Height() < 0) { + assert(mapBlockIndex.size() <= 1); + return; + } + + // Build forward-pointing map of the entire block tree. + std::multimap forward; + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + forward.insert(std::make_pair(it->second->pprev, it->second)); + } + + assert(forward.size() == mapBlockIndex.size()); + + std::pair::iterator, std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); + CBlockIndex* pindex = rangeGenesis.first->second; + rangeGenesis.first++; + assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. + + // Iterate over the entire block tree, using depth-first search. + // Along the way, remember whether there are blocks on the path from genesis + // block being explored which are the first to have certain properties. + size_t nNodes = 0; + int nHeight = 0; + CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. + CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. + CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). + CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). + CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). + while (pindex != NULL) { + nNodes++; + if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; + if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; + if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; + + // Begin: actual consistency checks. + if (pindex->pprev == NULL) { + // Genesis block checks. + assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match. + assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. + } + // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) + assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); + assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); + if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked + // All parents having data is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. + assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. + assert(pindex->nHeight == nHeight); // nHeight must be consistent. + assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. + assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. + assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid + if (pindexFirstInvalid == NULL) { + // Checks for not-invalid blocks. + assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. + } + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) { + if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates. + assert(setBlockIndexCandidates.count(pindex)); + } + } else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates. + assert(setBlockIndexCandidates.count(pindex) == 0); + } + // Check whether this block is in mapBlocksUnlinked. + std::pair::iterator, std::multimap::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); + bool foundInUnlinked = false; + while (rangeUnlinked.first != rangeUnlinked.second) { + assert(rangeUnlinked.first->first == pindex->pprev); + if (rangeUnlinked.first->second == pindex) { + foundInUnlinked = true; + break; + } + rangeUnlinked.first++; + } + if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) { + if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked. + assert(foundInUnlinked); + } + } else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked. + assert(!foundInUnlinked); + } + // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow + // End: actual consistency checks. + + // Try descending into the first subnode. + std::pair::iterator, std::multimap::iterator> range = forward.equal_range(pindex); + if (range.first != range.second) { + // A subnode was found. + pindex = range.first->second; + nHeight++; + continue; + } + // This is a leaf node. + // Move upwards until we reach a node of which we have not yet visited the last child. + while (pindex) { + // We are going to either move to a parent or a sibling of pindex. + // If pindex was the first with a certain property, unset the corresponding variable. + if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; + if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; + if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; + if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; + if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; + // Find our parent. + CBlockIndex* pindexPar = pindex->pprev; + // Find which child we just visited. + std::pair::iterator, std::multimap::iterator> rangePar = forward.equal_range(pindexPar); + while (rangePar.first->second != pindex) { + assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child. + rangePar.first++; + } + // Proceed to the next one. + rangePar.first++; + if (rangePar.first != rangePar.second) { + // Move to the sibling. + pindex = rangePar.first->second; + break; + } else { + // Move up further. + pindex = pindexPar; + nHeight--; + continue; + } + } + } + + // Check that we actually traversed the entire map. + assert(nNodes == forward.size()); } ////////////////////////////////////////////////////////////////////////////// @@ -5588,49 +5417,48 @@ void static CheckBlockIndex() string GetWarnings(string strFor) { - int nPriority = 0; - string strStatusBar; - string strRPC; - - if (!CLIENT_VERSION_IS_RELEASE) - strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for staking or merchant applications!"); - - if (GetBoolArg("-testsafemode", false)) - strStatusBar = strRPC = "testsafemode enabled"; - - // Misc warnings like out of disk space and clock is wrong - if (strMiscWarning != "") { - nPriority = 1000; - strStatusBar = strMiscWarning; - } - - if (fLargeWorkForkFound) { - nPriority = 2000; - strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); - } - else if (fLargeWorkInvalidChainFound) { - nPriority = 2000; - strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); - } - - // 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"; + int nPriority = 0; + string strStatusBar; + string strRPC; + + if (!CLIENT_VERSION_IS_RELEASE) + strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for staking or merchant applications!"); + + if (GetBoolArg("-testsafemode", false)) + strStatusBar = strRPC = "testsafemode enabled"; + + // Misc warnings like out of disk space and clock is wrong + if (strMiscWarning != "") { + nPriority = 1000; + strStatusBar = strMiscWarning; + } + + if (fLargeWorkForkFound) { + nPriority = 2000; + strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); + } else if (fLargeWorkInvalidChainFound) { + nPriority = 2000; + strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); + } + + // 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"; } @@ -5642,1151 +5470,1127 @@ string GetWarnings(string strFor) bool static AlreadyHave(const CInv& inv) { - switch (inv.type) { - case MSG_TX: { - bool txInMap = false; - txInMap = mempool.exists(inv.hash); - return txInMap || mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); - } - case MSG_DSTX: - return mapObfuscationBroadcastTxes.count(inv.hash); - case MSG_BLOCK: - return mapBlockIndex.count(inv.hash); - case MSG_TXLOCK_REQUEST: - return mapTxLockReq.count(inv.hash) || - mapTxLockReqRejected.count(inv.hash); - case MSG_TXLOCK_VOTE: - return mapTxLockVote.count(inv.hash); - case MSG_SPORK: - return mapSporks.count(inv.hash); - case MSG_MASTERNODE_WINNER: - if (masternodePayments.mapMasternodePayeeVotes.count(inv.hash)) { - masternodeSync.AddedMasternodeWinner(inv.hash); - return true; - } - return false; - case MSG_BUDGET_VOTE: - if (budget.mapSeenMasternodeBudgetVotes.count(inv.hash)) { - masternodeSync.AddedBudgetItem(inv.hash); - return true; - } - return false; - case MSG_BUDGET_PROPOSAL: - if (budget.mapSeenMasternodeBudgetProposals.count(inv.hash)) { - masternodeSync.AddedBudgetItem(inv.hash); - return true; - } - return false; - case MSG_BUDGET_FINALIZED_VOTE: - if (budget.mapSeenFinalizedBudgetVotes.count(inv.hash)) { - masternodeSync.AddedBudgetItem(inv.hash); - return true; - } - return false; - case MSG_BUDGET_FINALIZED: - if (budget.mapSeenFinalizedBudgets.count(inv.hash)) { - masternodeSync.AddedBudgetItem(inv.hash); - return true; - } - return false; - case MSG_MASTERNODE_ANNOUNCE: - if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { - masternodeSync.AddedMasternodeList(inv.hash); - return true; - } - return false; - case MSG_MASTERNODE_PING: - return mnodeman.mapSeenMasternodePing.count(inv.hash); - } - // Don't know what it is, just say we already got one - return true; + switch (inv.type) { + case MSG_TX: { + bool txInMap = false; + txInMap = mempool.exists(inv.hash); + return txInMap || mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); + } + case MSG_DSTX: + return mapObfuscationBroadcastTxes.count(inv.hash); + case MSG_BLOCK: + return mapBlockIndex.count(inv.hash); + case MSG_TXLOCK_REQUEST: + return mapTxLockReq.count(inv.hash) || + mapTxLockReqRejected.count(inv.hash); + case MSG_TXLOCK_VOTE: + return mapTxLockVote.count(inv.hash); + case MSG_SPORK: + return mapSporks.count(inv.hash); + case MSG_MASTERNODE_WINNER: + if (masternodePayments.mapMasternodePayeeVotes.count(inv.hash)) { + masternodeSync.AddedMasternodeWinner(inv.hash); + return true; + } + return false; + case MSG_BUDGET_VOTE: + if (budget.mapSeenMasternodeBudgetVotes.count(inv.hash)) { + masternodeSync.AddedBudgetItem(inv.hash); + return true; + } + return false; + case MSG_BUDGET_PROPOSAL: + if (budget.mapSeenMasternodeBudgetProposals.count(inv.hash)) { + masternodeSync.AddedBudgetItem(inv.hash); + return true; + } + return false; + case MSG_BUDGET_FINALIZED_VOTE: + if (budget.mapSeenFinalizedBudgetVotes.count(inv.hash)) { + masternodeSync.AddedBudgetItem(inv.hash); + return true; + } + return false; + case MSG_BUDGET_FINALIZED: + if (budget.mapSeenFinalizedBudgets.count(inv.hash)) { + masternodeSync.AddedBudgetItem(inv.hash); + return true; + } + return false; + case MSG_MASTERNODE_ANNOUNCE: + if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { + masternodeSync.AddedMasternodeList(inv.hash); + return true; + } + return false; + case MSG_MASTERNODE_PING: + return mnodeman.mapSeenMasternodePing.count(inv.hash); + } + // Don't know what it is, just say we already got one + return true; } void static ProcessGetData(CNode* pfrom) { - std::deque::iterator it = pfrom->vRecvGetData.begin(); - - vector vNotFound; - - LOCK(cs_main); - - 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) { - bool send = false; - BlockMap::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) { - if (chainActive.Contains(mi->second)) { - send = true; - } - else { - // To prevent fingerprinting attacks, only send blocks outside of the active - // chain if they are valid, and no more than a max reorg depth than the best header - // chain we know about. - send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (chainActive.Height() - mi->second->nHeight < Params().MaxReorganizationDepth()); - if (!send) { - LogPrintf("ProcessGetData(): ignoring request from peer=%i for old block that isn't in the main chain\n", pfrom->GetId()); - } - } - } - // Don't send not-validated blocks - if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) { - // Send block from disk - CBlock block; - if (!ReadBlockFromDisk(block, (*mi).second)) - assert(!"cannot load block from disk"); - 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, chainActive.Tip()->GetBlockHash())); - 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) { - CTransaction tx; - if (mempool.lookup(inv.hash, tx)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << tx; - pfrom->PushMessage("tx", ss); - pushed = true; - } - } - if (!pushed && inv.type == MSG_TXLOCK_VOTE) { - if (mapTxLockVote.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapTxLockVote[inv.hash]; - pfrom->PushMessage("txlvote", ss); - pushed = true; - } - } - if (!pushed && inv.type == MSG_TXLOCK_REQUEST) { - if (mapTxLockReq.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapTxLockReq[inv.hash]; - pfrom->PushMessage("ix", ss); - pushed = true; - } - } - if (!pushed && inv.type == MSG_SPORK) { - if (mapSporks.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapSporks[inv.hash]; - pfrom->PushMessage("spork", ss); - pushed = true; - } - } - if (!pushed && inv.type == MSG_MASTERNODE_WINNER) { - if (masternodePayments.mapMasternodePayeeVotes.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << masternodePayments.mapMasternodePayeeVotes[inv.hash]; - pfrom->PushMessage("mnw", ss); - pushed = true; - } - } - if (!pushed && inv.type == MSG_BUDGET_VOTE) { - if (budget.mapSeenMasternodeBudgetVotes.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << budget.mapSeenMasternodeBudgetVotes[inv.hash]; - pfrom->PushMessage("mvote", ss); - pushed = true; - } - } - - if (!pushed && inv.type == MSG_BUDGET_PROPOSAL) { - if (budget.mapSeenMasternodeBudgetProposals.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << budget.mapSeenMasternodeBudgetProposals[inv.hash]; - pfrom->PushMessage("mprop", ss); - pushed = true; - } - } - - if (!pushed && inv.type == MSG_BUDGET_FINALIZED_VOTE) { - if (budget.mapSeenFinalizedBudgetVotes.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << budget.mapSeenFinalizedBudgetVotes[inv.hash]; - pfrom->PushMessage("fbvote", ss); - pushed = true; - } - } - - if (!pushed && inv.type == MSG_BUDGET_FINALIZED) { - if (budget.mapSeenFinalizedBudgets.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << budget.mapSeenFinalizedBudgets[inv.hash]; - pfrom->PushMessage("fbs", ss); - pushed = true; - } - } - - if (!pushed && inv.type == MSG_MASTERNODE_ANNOUNCE) { - if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mnodeman.mapSeenMasternodeBroadcast[inv.hash]; - pfrom->PushMessage("mnb", ss); - pushed = true; - } - } - - if (!pushed && inv.type == MSG_MASTERNODE_PING) { - if (mnodeman.mapSeenMasternodePing.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mnodeman.mapSeenMasternodePing[inv.hash]; - pfrom->PushMessage("mnp", ss); - pushed = true; - } - } - - if (!pushed && inv.type == MSG_DSTX) { - if (mapObfuscationBroadcastTxes.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapObfuscationBroadcastTxes[inv.hash].tx << mapObfuscationBroadcastTxes[inv.hash].vin << mapObfuscationBroadcastTxes[inv.hash].vchSig << mapObfuscationBroadcastTxes[inv.hash].sigTime; - - pfrom->PushMessage("dstx", ss); - pushed = true; - } - } - - - if (!pushed) { - vNotFound.push_back(inv); - } - } - - // Track requests for our stuff. - g_signals.Inventory(inv.hash); - - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) - break; - } - } - - 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); - } + std::deque::iterator it = pfrom->vRecvGetData.begin(); + + vector vNotFound; + + LOCK(cs_main); + + 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) { + bool send = false; + BlockMap::iterator mi = mapBlockIndex.find(inv.hash); + if (mi != mapBlockIndex.end()) { + if (chainActive.Contains(mi->second)) { + send = true; + } else { + // To prevent fingerprinting attacks, only send blocks outside of the active + // chain if they are valid, and no more than a max reorg depth than the best header + // chain we know about. + send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && + (chainActive.Height() - mi->second->nHeight < Params().MaxReorganizationDepth()); + if (!send) { + LogPrintf("ProcessGetData(): ignoring request from peer=%i for old block that isn't in the main chain\n", pfrom->GetId()); + } + } + } + // Don't send not-validated blocks + if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) { + // Send block from disk + CBlock block; + if (!ReadBlockFromDisk(block, (*mi).second)) + assert(!"cannot load block from disk"); + 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, chainActive.Tip()->GetBlockHash())); + 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) { + CTransaction tx; + if (mempool.lookup(inv.hash, tx)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << tx; + pfrom->PushMessage("tx", ss); + pushed = true; + } + } + if (!pushed && inv.type == MSG_TXLOCK_VOTE) { + if (mapTxLockVote.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapTxLockVote[inv.hash]; + pfrom->PushMessage("txlvote", ss); + pushed = true; + } + } + if (!pushed && inv.type == MSG_TXLOCK_REQUEST) { + if (mapTxLockReq.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapTxLockReq[inv.hash]; + pfrom->PushMessage("ix", ss); + pushed = true; + } + } + if (!pushed && inv.type == MSG_SPORK) { + if (mapSporks.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapSporks[inv.hash]; + pfrom->PushMessage("spork", ss); + pushed = true; + } + } + if (!pushed && inv.type == MSG_MASTERNODE_WINNER) { + if (masternodePayments.mapMasternodePayeeVotes.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << masternodePayments.mapMasternodePayeeVotes[inv.hash]; + pfrom->PushMessage("mnw", ss); + pushed = true; + } + } + if (!pushed && inv.type == MSG_BUDGET_VOTE) { + if (budget.mapSeenMasternodeBudgetVotes.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << budget.mapSeenMasternodeBudgetVotes[inv.hash]; + pfrom->PushMessage("mvote", ss); + pushed = true; + } + } + + if (!pushed && inv.type == MSG_BUDGET_PROPOSAL) { + if (budget.mapSeenMasternodeBudgetProposals.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << budget.mapSeenMasternodeBudgetProposals[inv.hash]; + pfrom->PushMessage("mprop", ss); + pushed = true; + } + } + + if (!pushed && inv.type == MSG_BUDGET_FINALIZED_VOTE) { + if (budget.mapSeenFinalizedBudgetVotes.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << budget.mapSeenFinalizedBudgetVotes[inv.hash]; + pfrom->PushMessage("fbvote", ss); + pushed = true; + } + } + + if (!pushed && inv.type == MSG_BUDGET_FINALIZED) { + if (budget.mapSeenFinalizedBudgets.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << budget.mapSeenFinalizedBudgets[inv.hash]; + pfrom->PushMessage("fbs", ss); + pushed = true; + } + } + + if (!pushed && inv.type == MSG_MASTERNODE_ANNOUNCE) { + if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mnodeman.mapSeenMasternodeBroadcast[inv.hash]; + pfrom->PushMessage("mnb", ss); + pushed = true; + } + } + + if (!pushed && inv.type == MSG_MASTERNODE_PING) { + if (mnodeman.mapSeenMasternodePing.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mnodeman.mapSeenMasternodePing[inv.hash]; + pfrom->PushMessage("mnp", ss); + pushed = true; + } + } + + if (!pushed && inv.type == MSG_DSTX) { + if (mapObfuscationBroadcastTxes.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapObfuscationBroadcastTxes[inv.hash].tx << mapObfuscationBroadcastTxes[inv.hash].vin << mapObfuscationBroadcastTxes[inv.hash].vchSig << mapObfuscationBroadcastTxes[inv.hash].sigTime; + + pfrom->PushMessage("dstx", ss); + pushed = true; + } + } + + + if (!pushed) { + vNotFound.push_back(inv); + } + } + + // Track requests for our stuff. + g_signals.Inventory(inv.hash); + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + break; + } + } + + 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, int64_t nTimeReceived) { - RandAddSeedPerfmon(); - if (fDebug) - LogPrintf("received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { - LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); - return true; - } - - if (strCommand == "version") { - // Each connection can only send one version message - if (pfrom->nVersion != 0) { - pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); - Misbehaving(pfrom->GetId(), 1); - return false; - } - - // Ccbc: We use certain sporks during IBD, so check to see if they are - // available. If not, ask the first peer connected for them. - if (!pSporkDB->SporkExists(SPORK_14_NEW_PROTOCOL_ENFORCEMENT) && - !pSporkDB->SporkExists(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2) && - !pSporkDB->SporkExists(SPORK_17_TREASURY_PAYMENT_ENFORCEMENT_DEFAULT) && - !pSporkDB->SporkExists(SPORK_18_REVIVE_PAYMENT_ENFORCEMENT_DEFAULT) && - !pSporkDB->SporkExists(SPORK_19_NEW_PROTOCOL_ENFORCEMENT_3) && - !pSporkDB->SporkExists(SPORK_11_LOCK_INVALID_UTXO) && - !pSporkDB->SporkExists(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) { - LogPrintf("Required sporks not found, asking peer to send them\n"); - pfrom->PushMessage("getsporks"); - } - - int64_t nTime; - CAddress addrMe; - CAddress addrFrom; - uint64_t nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; - if (pfrom->DisconnectOldProtocol(ActiveProtocol(), strCommand)) - return false; - - if (pfrom->nVersion == 10300) - pfrom->nVersion = 300; - if (!vRecv.empty()) - vRecv >> addrFrom >> nNonce; - if (!vRecv.empty()) { - vRecv >> LIMITED_STRING(pfrom->strSubVer, 256); - pfrom->cleanSubVer = SanitizeString(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; - - // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce && nNonce > 1) { - LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); - pfrom->fDisconnect = true; - return true; - } - - pfrom->addrLocal = addrMe; - if (pfrom->fInbound && addrMe.IsRoutable()) { - SeenLocal(addrMe); - } - - // Be shy and don't send version until we hear - if (pfrom->fInbound) - pfrom->PushVersion(); - - pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - - // Potentially mark this peer as a preferred download peer. - UpdatePreferredDownload(pfrom, State(pfrom->GetId())); - - // Change version - pfrom->PushMessage("verack"); - pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - - if (!pfrom->fInbound) { - // Advertise our address - if (fListen && !IsInitialBlockDownload()) { - CAddress addr = GetLocalAddress(&pfrom->addr); - if (addr.IsRoutable()) { - LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); - pfrom->PushAddress(addr); - } - else if (IsPeerAddrLocalGood(pfrom)) { - addr.SetIP(pfrom->addrLocal); - LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); - 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); - } - - pfrom->fSuccessfullyConnected = true; - - string remoteAddr; - if (fLogIPs) - remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); - - LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", - pfrom->cleanSubVer, pfrom->nVersion, - pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, - remoteAddr); - - AddTimeData(pfrom->addr, nTime); - } - - - else if (pfrom->nVersion == 0) { - // Must have a version message before anything else - Misbehaving(pfrom->GetId(), 1); - return false; - } - - - else if (strCommand == "verack") { - pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - - // Mark this node as currently connected, so we update its timestamp later. - if (pfrom->fNetworkNode) { - LOCK(cs_main); - State(pfrom->GetId())->fCurrentlyConnected = true; - } - } - - - 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) { - Misbehaving(pfrom->GetId(), 20); - return error("message addr size() = %u", vAddr.size()); - } - - // Store the new addresses - vector vAddrOk; - int64_t nNow = GetAdjustedTime(); - int64_t 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_t 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) { - Misbehaving(pfrom->GetId(), 20); - return error("message inv size() = %u", vInv.size()); - } - - LOCK(cs_main); - - std::vector vToFetch; - - 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); - LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); - - if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) - pfrom->AskFor(inv); - - - if (inv.type == MSG_BLOCK) { - UpdateBlockAvailability(pfrom->GetId(), inv.hash); - if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { - // Add this to the list of blocks to request - vToFetch.push_back(inv); - LogPrint("net", "getblocks (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); - } - } - - // Track requests for our stuff - g_signals.Inventory(inv.hash); - - if (pfrom->nSendSize > (SendBufferSize() * 2)) { - Misbehaving(pfrom->GetId(), 50); - return error("send buffer size() = %u", pfrom->nSendSize); - } - } - - if (!vToFetch.empty()) - pfrom->PushMessage("getdata", vToFetch); - } - - - else if (strCommand == "getdata") { - vector vInv; - vRecv >> vInv; - if (vInv.size() > MAX_INV_SZ) { - Misbehaving(pfrom->GetId(), 20); - return error("message getdata size() = %u", vInv.size()); - } - - if (fDebug || (vInv.size() != 1)) - LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); - - if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) - LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); - - pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom); - } - - - else if (strCommand == "getblocks" || strCommand == "getheaders") { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - LOCK(cs_main); - - // Find the last block the caller has in the main chain - CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); - - // Send the rest of the chain - if (pindex) - pindex = chainActive.Next(pindex); - int nLimit = 500; - LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop == uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id); - for (; pindex; pindex = chainActive.Next(pindex)) { - if (pindex->GetBlockHash() == hashStop) { - LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); - 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. - LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); - pfrom->hashContinue = pindex->GetBlockHash(); - break; - } - } - } - - - else if (strCommand == "headers" && Params().HeadersFirstSyncingActive()) { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - LOCK(cs_main); - - if (IsInitialBlockDownload()) - return true; - - CBlockIndex* pindex = NULL; - if (locator.IsNull()) { - // If locator is null, return the hashStop block - BlockMap::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 = FindForkInGlobalIndex(chainActive, locator); - if (pindex) - pindex = chainActive.Next(pindex); - } - - // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end - vector vHeaders; - int nLimit = MAX_HEADERS_RESULTS; - if (fDebug) - LogPrintf("getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); - for (; pindex; pindex = chainActive.Next(pindex)) { - vHeaders.push_back(pindex->GetBlockHeader()); - if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) - break; - } - pfrom->PushMessage("headers", vHeaders); - } - - - else if (strCommand == "tx" || strCommand == "dstx") { - vector vWorkQueue; - vector vEraseQueue; - CTransaction tx; - - //masternode signed transaction - bool ignoreFees = false; - CTxIn vin; - vector vchSig; - int64_t sigTime; - - if (strCommand == "tx") { - vRecv >> tx; - } - else if (strCommand == "dstx") { - //these allow masternodes to publish a limited amount of free transactions - vRecv >> tx >> vin >> vchSig >> sigTime; - - CMasternode* pmn = mnodeman.Find(vin); - if (pmn != NULL) { - if (!pmn->allowFreeTx) { - //multiple peers can send us a valid masternode transaction - if (fDebug) LogPrintf("dstx: Masternode sending too many transactions %s\n", tx.GetHash().ToString()); - return true; - } - - std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); - - std::string errorMessage = ""; - if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { - LogPrintf("dstx: Got bad masternode address signature %s \n", vin.ToString()); - //pfrom->Misbehaving(20); - return false; - } - - LogPrintf("dstx: Got Masternode transaction %s\n", tx.GetHash().ToString()); - - ignoreFees = true; - pmn->allowFreeTx = false; - - if (!mapObfuscationBroadcastTxes.count(tx.GetHash())) { - CObfuscationBroadcastTx dstx; - dstx.tx = tx; - dstx.vin = vin; - dstx.vchSig = vchSig; - dstx.sigTime = sigTime; - - mapObfuscationBroadcastTxes.insert(make_pair(tx.GetHash(), dstx)); - } - } - } - - CInv inv(MSG_TX, tx.GetHash()); - pfrom->AddInventoryKnown(inv); - - LOCK(cs_main); - - bool fMissingInputs = false; - bool fMissingZerocoinInputs = false; - CValidationState state; - - mapAlreadyAskedFor.erase(inv); - - if (!tx.IsZerocoinSpend() && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, false, ignoreFees)) { - mempool.check(pcoinsTip); - RelayTransaction(tx); - vWorkQueue.push_back(inv.hash); - - LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, - tx.GetHash().ToString(), - mempool.mapTx.size()); - - // Recursively process any orphan transactions that depended on this one - set setMisbehaving; - for (unsigned int i = 0; i < vWorkQueue.size(); i++) { - map >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); - if (itByPrev == mapOrphanTransactionsByPrev.end()) - continue; - for (set::iterator mi = itByPrev->second.begin(); - mi != itByPrev->second.end(); - ++mi) { - const uint256& orphanHash = *mi; - const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx; - NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer; - 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 (setMisbehaving.count(fromPeer)) - continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { - LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx); - vWorkQueue.push_back(orphanHash); - vEraseQueue.push_back(orphanHash); - } - else if (!fMissingInputs2) { - int nDos = 0; - if (stateDummy.IsInvalid(nDos) && nDos > 0) { - // Punish peer that gave us an invalid orphan tx - Misbehaving(fromPeer, nDos); - setMisbehaving.insert(fromPeer); - LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); - } - // Has inputs but not accepted to mempool - // Probably non-standard or insufficient fee/priority - LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); - vEraseQueue.push_back(orphanHash); - } - mempool.check(pcoinsTip); - } - } - - BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); - } - else if (tx.IsZerocoinSpend() && AcceptToMemoryPool(mempool, state, tx, true, &fMissingZerocoinInputs, false, ignoreFees)) { - //Presstab: ZCoin has a bunch of code commented out here. Is this something that should have more going on? - //Also there is nothing that handles fMissingZerocoinInputs. Does there need to be? - RelayTransaction(tx); - LogPrint("mempool", "AcceptToMemoryPool: Zerocoinspend peer=%d %s : accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, - tx.GetHash().ToString(), - mempool.mapTx.size()); - } - else if (fMissingInputs) { - AddOrphanTx(tx, pfrom->GetId()); - - // DoS prevention: do not allow mapOrphanTransactions to grow unbounded - unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); - if (nEvicted > 0) - LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); - } - else if (pfrom->fWhitelisted) { - // Always relay transactions received from whitelisted peers, even - // if they are already in the mempool (allowing the node to function - // as a gateway for nodes hidden behind it). - - RelayTransaction(tx); - } - - if (strCommand == "dstx") { - CInv inv(MSG_DSTX, tx.GetHash()); - RelayInv(inv); - } - - int nDoS = 0; - if (state.IsInvalid(nDoS)) { - LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), - pfrom->id, pfrom->cleanSubVer, - state.GetRejectReason()); - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); - } - } - - - else if (strCommand == "headers" && Params().HeadersFirstSyncingActive() && !fImporting && !fReindex) // Ignore headers received while importing - { - std::vector headers; - - // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. - unsigned int nCount = ReadCompactSize(vRecv); - if (nCount > MAX_HEADERS_RESULTS) { - Misbehaving(pfrom->GetId(), 20); - return error("headers message size = %u", nCount); - } - headers.resize(nCount); - for (unsigned int n = 0; n < nCount; n++) { - vRecv >> headers[n]; - ReadCompactSize(vRecv); // ignore tx count; assume it is 0. - } - - LOCK(cs_main); - - if (nCount == 0) { - // Nothing interesting. Stop asking this peers for more headers. - return true; - } - CBlockIndex* pindexLast = NULL; - BOOST_FOREACH(const CBlockHeader& header, headers) { - CValidationState state; - if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) { - Misbehaving(pfrom->GetId(), 20); - return error("non-continuous headers sequence"); - } - - /*TODO: this has a CBlock cast on it so that it will compile. There should be a solution for this - * before headers are reimplemented on mainnet - */ - if (!AcceptBlockHeader((CBlock)header, state, &pindexLast)) { - int nDoS; - if (state.IsInvalid(nDoS)) { - if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); - std::string strError = "invalid header received " + header.GetHash().ToString(); - return error(strError.c_str()); - } - } - } - - if (pindexLast) - UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - - if (nCount == MAX_HEADERS_RESULTS && pindexLast) { - // Headers message had its maximum size; the peer may have more headers. - // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue - // from there instead. - LogPrintf("more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); - pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256(0)); - } - - CheckBlockIndex(); - } - - else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing - { - CBlock block; - vRecv >> block; - uint256 hashBlock = block.GetHash(); - CInv inv(MSG_BLOCK, hashBlock); - LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - - //sometimes we will be sent their most recent block and its not the one we want, in that case tell where we are - if (!mapBlockIndex.count(block.hashPrevBlock)) { - if (find(pfrom->vBlockRequested.begin(), pfrom->vBlockRequested.end(), hashBlock) != pfrom->vBlockRequested.end()) { - //we already asked for this block, so lets work backwards and ask for the previous block - pfrom->PushMessage("getblocks", chainActive.GetLocator(), block.hashPrevBlock); - pfrom->vBlockRequested.push_back(block.hashPrevBlock); - } - else { - //ask to sync to this block - pfrom->PushMessage("getblocks", chainActive.GetLocator(), hashBlock); - pfrom->vBlockRequested.push_back(hashBlock); - } - } - else { - pfrom->AddInventoryKnown(inv); - - CValidationState state; - if (!mapBlockIndex.count(block.GetHash())) { - ProcessNewBlock(state, pfrom, &block); - int nDoS; - if (state.IsInvalid(nDoS)) { - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) { - TRY_LOCK(cs_main, lockMain); - if (lockMain) Misbehaving(pfrom->GetId(), nDoS); - } - } - //disconnect this node if its old protocol version - pfrom->DisconnectOldProtocol(ActiveProtocol(), strCommand); - } - else { - LogPrint("net", "%s : Already processed block %s, skipping ProcessNewBlock()\n", __func__, block.GetHash().GetHex()); - } - } - } - - - // This asymmetric behavior for inbound and outbound connections was introduced - // to prevent a fingerprinting attack: an attacker can send specific fake addresses - // to users' AddrMan and later request them by sending getaddr messages. - // Making users (which are behind NAT and can only make outgoing connections) ignore - // getaddr message mitigates the attack. - else if ((strCommand == "getaddr") && (pfrom->fInbound)) { - pfrom->vAddrToSend.clear(); - vector vAddr = addrman.GetAddr(); - BOOST_FOREACH(const CAddress& addr, vAddr) - pfrom->PushAddress(addr); - } - - - else if (strCommand == "mempool") { - LOCK2(cs_main, pfrom->cs_filter); - - std::vector vtxid; - mempool.queryHashes(vtxid); - vector vInv; - BOOST_FOREACH(uint256& hash, vtxid) { - CInv inv(MSG_TX, hash); - CTransaction tx; - bool fInMemPool = mempool.lookup(hash, tx); - if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || - (!pfrom->pfilter)) - vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) { - pfrom->PushMessage("inv", vInv); - vInv.clear(); - } - } - if (vInv.size() > 0) - pfrom->PushMessage("inv", vInv); - } - - - else if (strCommand == "ping") { - if (pfrom->nVersion > BIP0031_VERSION) { - uint64_t 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 == "pong") { - int64_t pingUsecEnd = nTimeReceived; - uint64_t nonce = 0; - size_t nAvail = vRecv.in_avail(); - bool bPingFinished = false; - std::string sProblem; - - if (nAvail >= sizeof(nonce)) { - vRecv >> nonce; - - // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) - if (pfrom->nPingNonceSent != 0) { - if (nonce == pfrom->nPingNonceSent) { - // Matching pong received, this ping is no longer outstanding - bPingFinished = true; - int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; - if (pingUsecTime > 0) { - // Successful ping time measurement, replace previous - pfrom->nPingUsecTime = pingUsecTime; - } - else { - // This should never happen - sProblem = "Timing mishap"; - } - } - else { - // Nonce mismatches are normal when pings are overlapping - sProblem = "Nonce mismatch"; - if (nonce == 0) { - // This is most likely a bug in another implementation somewhere, cancel this ping - bPingFinished = true; - sProblem = "Nonce zero"; - } - } - } - else { - sProblem = "Unsolicited pong without ping"; - } - } - else { - // This is most likely a bug in another implementation somewhere, cancel this ping - bPingFinished = true; - sProblem = "Short payload"; - } - - if (!(sProblem.empty())) { - LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", - pfrom->id, - pfrom->cleanSubVer, - sProblem, - pfrom->nPingNonceSent, - nonce, - nAvail); - } - if (bPingFinished) { - pfrom->nPingNonceSent = 0; - } - } - - - else if (fAlerts && 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. - Misbehaving(pfrom->GetId(), 10); - } - } - } - - else if (!(nLocalServices & NODE_BLOOM) && - (strCommand == "filterload" || - strCommand == "filteradd" || - strCommand == "filterclear")) { - LogPrintf("bloom message=%s\n", strCommand); - Misbehaving(pfrom->GetId(), 100); - } - - else if (strCommand == "filterload") { - CBloomFilter filter; - vRecv >> filter; - - if (!filter.IsWithinSizeConstraints()) - // There is no excuse for sending a too-large filter - Misbehaving(pfrom->GetId(), 100); - else { - LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(filter); - pfrom->pfilter->UpdateEmptyFull(); - } - 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) { - Misbehaving(pfrom->GetId(), 100); - } - else { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) - pfrom->pfilter->insert(vData); - else - Misbehaving(pfrom->GetId(), 100); - } - } - - - else if (strCommand == "filterclear") { - LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(); - pfrom->fRelayTxes = true; - } - - - else if (strCommand == "reject") { - if (fDebug) { - try { - string strMsg; - unsigned char ccode; - string strReason; - vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); - - ostringstream ss; - ss << strMsg << " code " << itostr(ccode) << ": " << strReason; - - if (strMsg == "block" || strMsg == "tx") { - uint256 hash; - vRecv >> hash; - ss << ": hash " << hash.ToString(); - } - LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); - } - catch (std::ios_base::failure& e) { - // Avoid feedback loops by preventing reject messages from triggering a new reject message. - LogPrint("net", "Unparseable reject message received\n"); - } - } - } - else { - //probably one the extensions - obfuScationPool.ProcessMessageObfuscation(pfrom, strCommand, vRecv); - mnodeman.ProcessMessage(pfrom, strCommand, vRecv); - budget.ProcessMessage(pfrom, strCommand, vRecv); - masternodePayments.ProcessMessageMasternodePayments(pfrom, strCommand, vRecv); - ProcessMessageSwiftTX(pfrom, strCommand, vRecv); - ProcessSpork(pfrom, strCommand, vRecv); - masternodeSync.ProcessMessage(pfrom, strCommand, vRecv); - } + RandAddSeedPerfmon(); + if (fDebug) + LogPrintf("received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { + LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); + return true; + } + + if (strCommand == "version") { + // Each connection can only send one version message + if (pfrom->nVersion != 0) { + pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + Misbehaving(pfrom->GetId(), 1); + return false; + } + + // Ccbc: We use certain sporks during IBD, so check to see if they are + // available. If not, ask the first peer connected for them. + if (!pSporkDB->SporkExists(SPORK_14_NEW_PROTOCOL_ENFORCEMENT) && + !pSporkDB->SporkExists(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2) && + !pSporkDB->SporkExists(SPORK_11_LOCK_INVALID_UTXO) && + !pSporkDB->SporkExists(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) { + LogPrintf("Required sporks not found, asking peer to send them\n"); + pfrom->PushMessage("getsporks"); + } + + int64_t nTime; + CAddress addrMe; + CAddress addrFrom; + uint64_t nNonce = 1; + vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->DisconnectOldProtocol(ActiveProtocol(), strCommand)) + return false; + + if (pfrom->nVersion == 10300) + pfrom->nVersion = 300; + if (!vRecv.empty()) + vRecv >> addrFrom >> nNonce; + if (!vRecv.empty()) { + vRecv >> LIMITED_STRING(pfrom->strSubVer, 256); + pfrom->cleanSubVer = SanitizeString(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; + + // Disconnect if we connected to ourself + if (nNonce == nLocalHostNonce && nNonce > 1) { + LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); + pfrom->fDisconnect = true; + return true; + } + + pfrom->addrLocal = addrMe; + if (pfrom->fInbound && addrMe.IsRoutable()) { + SeenLocal(addrMe); + } + + // Be shy and don't send version until we hear + if (pfrom->fInbound) + pfrom->PushVersion(); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + + // Potentially mark this peer as a preferred download peer. + UpdatePreferredDownload(pfrom, State(pfrom->GetId())); + + // Change version + pfrom->PushMessage("verack"); + pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + + if (!pfrom->fInbound) { + // Advertise our address + if (fListen && !IsInitialBlockDownload()) { + CAddress addr = GetLocalAddress(&pfrom->addr); + if (addr.IsRoutable()) { + LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); + pfrom->PushAddress(addr); + } else if (IsPeerAddrLocalGood(pfrom)) { + addr.SetIP(pfrom->addrLocal); + LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); + 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); + } + + pfrom->fSuccessfullyConnected = true; + + string remoteAddr; + if (fLogIPs) + remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); + + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", + pfrom->cleanSubVer, pfrom->nVersion, + pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, + remoteAddr); + + AddTimeData(pfrom->addr, nTime); + } + + + else if (pfrom->nVersion == 0) { + // Must have a version message before anything else + Misbehaving(pfrom->GetId(), 1); + return false; + } + + + else if (strCommand == "verack") { + pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + + // Mark this node as currently connected, so we update its timestamp later. + if (pfrom->fNetworkNode) { + LOCK(cs_main); + State(pfrom->GetId())->fCurrentlyConnected = true; + } + } + + + 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) { + Misbehaving(pfrom->GetId(), 20); + return error("message addr size() = %u", vAddr.size()); + } + + // Store the new addresses + vector vAddrOk; + int64_t nNow = GetAdjustedTime(); + int64_t 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_t 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) { + Misbehaving(pfrom->GetId(), 20); + return error("message inv size() = %u", vInv.size()); + } + + LOCK(cs_main); + + std::vector vToFetch; + + 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); + LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); + + if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) + pfrom->AskFor(inv); + + + if (inv.type == MSG_BLOCK) { + UpdateBlockAvailability(pfrom->GetId(), inv.hash); + if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { + // Add this to the list of blocks to request + vToFetch.push_back(inv); + LogPrint("net", "getblocks (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); + } + } - return true; + // Track requests for our stuff + g_signals.Inventory(inv.hash); + + if (pfrom->nSendSize > (SendBufferSize() * 2)) { + Misbehaving(pfrom->GetId(), 50); + return error("send buffer size() = %u", pfrom->nSendSize); + } + } + + if (!vToFetch.empty()) + pfrom->PushMessage("getdata", vToFetch); + } + + + else if (strCommand == "getdata") { + vector vInv; + vRecv >> vInv; + if (vInv.size() > MAX_INV_SZ) { + Misbehaving(pfrom->GetId(), 20); + return error("message getdata size() = %u", vInv.size()); + } + + if (fDebug || (vInv.size() != 1)) + LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); + + if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) + LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); + + pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); + ProcessGetData(pfrom); + } + + + else if (strCommand == "getblocks" || strCommand == "getheaders") { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + LOCK(cs_main); + + // Find the last block the caller has in the main chain + CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); + + // Send the rest of the chain + if (pindex) + pindex = chainActive.Next(pindex); + int nLimit = 500; + LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop == uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id); + for (; pindex; pindex = chainActive.Next(pindex)) { + if (pindex->GetBlockHash() == hashStop) { + LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + 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. + LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + pfrom->hashContinue = pindex->GetBlockHash(); + break; + } + } + } + + + else if (strCommand == "headers" && Params().HeadersFirstSyncingActive()) { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + LOCK(cs_main); + + if (IsInitialBlockDownload()) + return true; + + CBlockIndex* pindex = NULL; + if (locator.IsNull()) { + // If locator is null, return the hashStop block + BlockMap::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 = FindForkInGlobalIndex(chainActive, locator); + if (pindex) + pindex = chainActive.Next(pindex); + } + + // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end + vector vHeaders; + int nLimit = MAX_HEADERS_RESULTS; + if (fDebug) + LogPrintf("getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); + for (; pindex; pindex = chainActive.Next(pindex)) { + vHeaders.push_back(pindex->GetBlockHeader()); + if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) + break; + } + pfrom->PushMessage("headers", vHeaders); + } + + + else if (strCommand == "tx" || strCommand == "dstx") { + vector vWorkQueue; + vector vEraseQueue; + CTransaction tx; + + //masternode signed transaction + bool ignoreFees = false; + CTxIn vin; + vector vchSig; + int64_t sigTime; + + if (strCommand == "tx") { + vRecv >> tx; + } else if (strCommand == "dstx") { + //these allow masternodes to publish a limited amount of free transactions + vRecv >> tx >> vin >> vchSig >> sigTime; + + CMasternode* pmn = mnodeman.Find(vin); + if (pmn != NULL) { + if (!pmn->allowFreeTx) { + //multiple peers can send us a valid masternode transaction + if (fDebug) LogPrintf("dstx: Masternode sending too many transactions %s\n", tx.GetHash().ToString()); + return true; + } + + std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); + + std::string errorMessage = ""; + if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { + LogPrintf("dstx: Got bad masternode address signature %s \n", vin.ToString()); + //pfrom->Misbehaving(20); + return false; + } + + LogPrintf("dstx: Got Masternode transaction %s\n", tx.GetHash().ToString()); + + ignoreFees = true; + pmn->allowFreeTx = false; + + if (!mapObfuscationBroadcastTxes.count(tx.GetHash())) { + CObfuscationBroadcastTx dstx; + dstx.tx = tx; + dstx.vin = vin; + dstx.vchSig = vchSig; + dstx.sigTime = sigTime; + + mapObfuscationBroadcastTxes.insert(make_pair(tx.GetHash(), dstx)); + } + } + } + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + LOCK(cs_main); + + bool fMissingInputs = false; + bool fMissingZerocoinInputs = false; + CValidationState state; + + mapAlreadyAskedFor.erase(inv); + + if (!tx.IsZerocoinSpend() && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, false, ignoreFees)) { + mempool.check(pcoinsTip); + RelayTransaction(tx); + vWorkQueue.push_back(inv.hash); + + LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n", + pfrom->id, pfrom->cleanSubVer, + tx.GetHash().ToString(), + mempool.mapTx.size()); + + // Recursively process any orphan transactions that depended on this one + set setMisbehaving; + for (unsigned int i = 0; i < vWorkQueue.size(); i++) { + map >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); + if (itByPrev == mapOrphanTransactionsByPrev.end()) + continue; + for (set::iterator mi = itByPrev->second.begin(); + mi != itByPrev->second.end(); + ++mi) { + const uint256& orphanHash = *mi; + const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx; + NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer; + 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 (setMisbehaving.count(fromPeer)) + continue; + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { + LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); + RelayTransaction(orphanTx); + vWorkQueue.push_back(orphanHash); + vEraseQueue.push_back(orphanHash); + } else if (!fMissingInputs2) { + int nDos = 0; + if (stateDummy.IsInvalid(nDos) && nDos > 0) { + // Punish peer that gave us an invalid orphan tx + Misbehaving(fromPeer, nDos); + setMisbehaving.insert(fromPeer); + LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); + } + // Has inputs but not accepted to mempool + // Probably non-standard or insufficient fee/priority + LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); + vEraseQueue.push_back(orphanHash); + } + mempool.check(pcoinsTip); + } + } + + BOOST_FOREACH (uint256 hash, vEraseQueue) + EraseOrphanTx(hash); + } else if (tx.IsZerocoinSpend() && AcceptToMemoryPool(mempool, state, tx, true, &fMissingZerocoinInputs, false, ignoreFees)) { + //Presstab: ZCoin has a bunch of code commented out here. Is this something that should have more going on? + //Also there is nothing that handles fMissingZerocoinInputs. Does there need to be? + RelayTransaction(tx); + LogPrint("mempool", "AcceptToMemoryPool: Zerocoinspend peer=%d %s : accepted %s (poolsz %u)\n", + pfrom->id, pfrom->cleanSubVer, + tx.GetHash().ToString(), + mempool.mapTx.size()); + } else if (fMissingInputs) { + AddOrphanTx(tx, pfrom->GetId()); + + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); + unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); + if (nEvicted > 0) + LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); + } else if (pfrom->fWhitelisted) { + // Always relay transactions received from whitelisted peers, even + // if they are already in the mempool (allowing the node to function + // as a gateway for nodes hidden behind it). + + RelayTransaction(tx); + } + + if (strCommand == "dstx") { + CInv inv(MSG_DSTX, tx.GetHash()); + RelayInv(inv); + } + + int nDoS = 0; + if (state.IsInvalid(nDoS)) { + LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), + pfrom->id, pfrom->cleanSubVer, + state.GetRejectReason()); + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + } + } + + + else if (strCommand == "headers" && Params().HeadersFirstSyncingActive() && !fImporting && !fReindex) // Ignore headers received while importing + { + std::vector headers; + + // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. + unsigned int nCount = ReadCompactSize(vRecv); + if (nCount > MAX_HEADERS_RESULTS) { + Misbehaving(pfrom->GetId(), 20); + return error("headers message size = %u", nCount); + } + headers.resize(nCount); + for (unsigned int n = 0; n < nCount; n++) { + vRecv >> headers[n]; + ReadCompactSize(vRecv); // ignore tx count; assume it is 0. + } + + LOCK(cs_main); + + if (nCount == 0) { + // Nothing interesting. Stop asking this peers for more headers. + return true; + } + CBlockIndex* pindexLast = NULL; + BOOST_FOREACH (const CBlockHeader& header, headers) { + CValidationState state; + if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) { + Misbehaving(pfrom->GetId(), 20); + return error("non-continuous headers sequence"); + } + + /*TODO: this has a CBlock cast on it so that it will compile. There should be a solution for this + * before headers are reimplemented on mainnet + */ + if (!AcceptBlockHeader((CBlock)header, state, &pindexLast)) { + int nDoS; + if (state.IsInvalid(nDoS)) { + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + std::string strError = "invalid header received " + header.GetHash().ToString(); + return error(strError.c_str()); + } + } + } + + if (pindexLast) + UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); + + if (nCount == MAX_HEADERS_RESULTS && pindexLast) { + // Headers message had its maximum size; the peer may have more headers. + // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue + // from there instead. + LogPrintf("more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); + pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256(0)); + } + + CheckBlockIndex(); + } + + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing + { + CBlock block; + vRecv >> block; + uint256 hashBlock = block.GetHash(); + CInv inv(MSG_BLOCK, hashBlock); + LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); + + //sometimes we will be sent their most recent block and its not the one we want, in that case tell where we are + if (!mapBlockIndex.count(block.hashPrevBlock)) { + if (find(pfrom->vBlockRequested.begin(), pfrom->vBlockRequested.end(), hashBlock) != pfrom->vBlockRequested.end()) { + //we already asked for this block, so lets work backwards and ask for the previous block + pfrom->PushMessage("getblocks", chainActive.GetLocator(), block.hashPrevBlock); + pfrom->vBlockRequested.push_back(block.hashPrevBlock); + } else { + //ask to sync to this block + pfrom->PushMessage("getblocks", chainActive.GetLocator(), hashBlock); + pfrom->vBlockRequested.push_back(hashBlock); + } + } else { + pfrom->AddInventoryKnown(inv); + + CValidationState state; + if (!mapBlockIndex.count(block.GetHash())) { + ProcessNewBlock(state, pfrom, &block); + int nDoS; + if (state.IsInvalid(nDoS)) { + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + if (nDoS > 0) { + TRY_LOCK(cs_main, lockMain); + if (lockMain) Misbehaving(pfrom->GetId(), nDoS); + } + } + //disconnect this node if its old protocol version + pfrom->DisconnectOldProtocol(ActiveProtocol(), strCommand); + } else { + LogPrint("net", "%s : Already processed block %s, skipping ProcessNewBlock()\n", __func__, block.GetHash().GetHex()); + } + } + } + + + // This asymmetric behavior for inbound and outbound connections was introduced + // to prevent a fingerprinting attack: an attacker can send specific fake addresses + // to users' AddrMan and later request them by sending getaddr messages. + // Making users (which are behind NAT and can only make outgoing connections) ignore + // getaddr message mitigates the attack. + else if ((strCommand == "getaddr") && (pfrom->fInbound)) { + pfrom->vAddrToSend.clear(); + vector vAddr = addrman.GetAddr(); + BOOST_FOREACH (const CAddress& addr, vAddr) + pfrom->PushAddress(addr); + } + + + else if (strCommand == "mempool") { + LOCK2(cs_main, pfrom->cs_filter); + + std::vector vtxid; + mempool.queryHashes(vtxid); + vector vInv; + BOOST_FOREACH (uint256& hash, vtxid) { + CInv inv(MSG_TX, hash); + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || + (!pfrom->pfilter)) + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) { + pfrom->PushMessage("inv", vInv); + vInv.clear(); + } + } + if (vInv.size() > 0) + pfrom->PushMessage("inv", vInv); + } + + + else if (strCommand == "ping") { + if (pfrom->nVersion > BIP0031_VERSION) { + uint64_t 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 == "pong") { + int64_t pingUsecEnd = nTimeReceived; + uint64_t nonce = 0; + size_t nAvail = vRecv.in_avail(); + bool bPingFinished = false; + std::string sProblem; + + if (nAvail >= sizeof(nonce)) { + vRecv >> nonce; + + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) + if (pfrom->nPingNonceSent != 0) { + if (nonce == pfrom->nPingNonceSent) { + // Matching pong received, this ping is no longer outstanding + bPingFinished = true; + int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; + if (pingUsecTime > 0) { + // Successful ping time measurement, replace previous + pfrom->nPingUsecTime = pingUsecTime; + } else { + // This should never happen + sProblem = "Timing mishap"; + } + } else { + // Nonce mismatches are normal when pings are overlapping + sProblem = "Nonce mismatch"; + if (nonce == 0) { + // This is most likely a bug in another implementation somewhere, cancel this ping + bPingFinished = true; + sProblem = "Nonce zero"; + } + } + } else { + sProblem = "Unsolicited pong without ping"; + } + } else { + // This is most likely a bug in another implementation somewhere, cancel this ping + bPingFinished = true; + sProblem = "Short payload"; + } + + if (!(sProblem.empty())) { + LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", + pfrom->id, + pfrom->cleanSubVer, + sProblem, + pfrom->nPingNonceSent, + nonce, + nAvail); + } + if (bPingFinished) { + pfrom->nPingNonceSent = 0; + } + } + + + else if (fAlerts && 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. + Misbehaving(pfrom->GetId(), 10); + } + } + } + + else if (!(nLocalServices & NODE_BLOOM) && + (strCommand == "filterload" || + strCommand == "filteradd" || + strCommand == "filterclear")) { + LogPrintf("bloom message=%s\n", strCommand); + Misbehaving(pfrom->GetId(), 100); + } + + else if (strCommand == "filterload") { + CBloomFilter filter; + vRecv >> filter; + + if (!filter.IsWithinSizeConstraints()) + // There is no excuse for sending a too-large filter + Misbehaving(pfrom->GetId(), 100); + else { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = new CBloomFilter(filter); + pfrom->pfilter->UpdateEmptyFull(); + } + 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) { + Misbehaving(pfrom->GetId(), 100); + } else { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + pfrom->pfilter->insert(vData); + else + Misbehaving(pfrom->GetId(), 100); + } + } + + + else if (strCommand == "filterclear") { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = new CBloomFilter(); + pfrom->fRelayTxes = true; + } + + + else if (strCommand == "reject") { + if (fDebug) { + try { + string strMsg; + unsigned char ccode; + string strReason; + vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); + + ostringstream ss; + ss << strMsg << " code " << itostr(ccode) << ": " << strReason; + + if (strMsg == "block" || strMsg == "tx") { + uint256 hash; + vRecv >> hash; + ss << ": hash " << hash.ToString(); + } + LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); + } catch (std::ios_base::failure& e) { + // Avoid feedback loops by preventing reject messages from triggering a new reject message. + LogPrint("net", "Unparseable reject message received\n"); + } + } + } else { + //probably one the extensions + obfuScationPool.ProcessMessageObfuscation(pfrom, strCommand, vRecv); + mnodeman.ProcessMessage(pfrom, strCommand, vRecv); + budget.ProcessMessage(pfrom, strCommand, vRecv); + masternodePayments.ProcessMessageMasternodePayments(pfrom, strCommand, vRecv); + ProcessMessageSwiftTX(pfrom, strCommand, vRecv); + ProcessSpork(pfrom, strCommand, vRecv); + masternodeSync.ProcessMessage(pfrom, strCommand, vRecv); + } + + + return true; } // Note: whenever a protocol update is needed toggle between both implementations (comment out the formerly active one) @@ -6795,448 +6599,440 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // it was the one which was commented out int ActiveProtocol() { - // SPORK_14 will remove early wallet adopters of protocol 70002 where max supply didnt have cap and - // seesaw masternode amount was set to 5k instead of 25k collateral - /* + // SPORK_14 will remove early wallet adopters of protocol 70002 where max supply didnt have cap and + // seesaw masternode amount was set to 5k instead of 25k collateral + /* if (IsSporkActive(SPORK_14_NEW_PROTOCOL_ENFORCEMENT)) return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; */ - // SPORK_15 will be used after SPORK_14 is used and commented out from being turned off. - // SPORK_15 has been turned on and will be commented out to prevent from being turned off. - // Approved by TFinch 11/29/2018 - /* + // SPORK_15 will be used after SPORK_14 is used and commented out from being turned off. + // SPORK_15 has been turned on and will be commented out to prevent from being turned off. + // Approved by TFinch 11/29/2018 + /* if (IsSporkActive(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2)) return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; */ - // SPORK_19 will be used after SPORK_15 is used and commented out from being turned off. - // This will be turned on after first of the year to enforce me spork privkey! - if (IsSporkActive(SPORK_19_NEW_PROTOCOL_ENFORCEMENT_3)) - return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; - return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; + // SPORK_19 will be used after SPORK_15 is used and commented out from being turned off. + // This will be turned on after first of the year to enforce me spork privkey! + if (IsSporkActive(SPORK_19_NEW_PROTOCOL_ENFORCEMENT_3)) + return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; + return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; } // requires LOCK(cs_vRecvMsg) bool ProcessMessages(CNode* pfrom) { - //if (fDebug) - // LogPrintf("ProcessMessages(%u 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); - - // this maintains the order of responses - if (!pfrom->vRecvGetData.empty()) return fOk; - - 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) - // LogPrintf("ProcessMessages(message %u msgsz, %u 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, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { - LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); - fOk = false; - break; - } - - // Read header - CMessageHeader& hdr = msg.hdr; - if (!hdr.IsValid()) { - LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); - 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) { - LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); - continue; - } - - // Process message - bool fRet = false; - try { - fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); - boost::this_thread::interruption_point(); - } - catch (std::ios_base::failure& e) { - pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message")); - if (strstr(e.what(), "end of data")) { - // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", SanitizeString(strCommand), nMessageSize, e.what()); - } - else if (strstr(e.what(), "size too large")) { - // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", SanitizeString(strCommand), 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) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", SanitizeString(strCommand), nMessageSize, pfrom->id); - - break; - } - - // In case the connection got shut down, its receive buffer was wiped - if (!pfrom->fDisconnect) - pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - - return fOk; + //if (fDebug) + // LogPrintf("ProcessMessages(%u 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); + + // this maintains the order of responses + if (!pfrom->vRecvGetData.empty()) return fOk; + + 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) + // LogPrintf("ProcessMessages(message %u msgsz, %u 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, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { + LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); + fOk = false; + break; + } + + // Read header + CMessageHeader& hdr = msg.hdr; + if (!hdr.IsValid()) { + LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); + 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) { + LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); + continue; + } + + // Process message + bool fRet = false; + try { + fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); + boost::this_thread::interruption_point(); + } catch (std::ios_base::failure& e) { + pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message")); + if (strstr(e.what(), "end of data")) { + // Allow exceptions from under-length message on vRecv + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", SanitizeString(strCommand), nMessageSize, e.what()); + } else if (strstr(e.what(), "size too large")) { + // Allow exceptions from over-long size + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", SanitizeString(strCommand), 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) + LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", SanitizeString(strCommand), nMessageSize, pfrom->id); + + break; + } + + // 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) { - { - // Don't send anything until we get their version message - if (pto->nVersion == 0) - return true; - - // - // Message: ping - // - bool pingSend = false; - if (pto->fPingQueued) { - // RPC ping request by user - pingSend = true; - } - if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { - // Ping automatically sent as a latency probe & keepalive. - pingSend = true; - } - if (pingSend) { - uint64_t nonce = 0; - while (nonce == 0) { - GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); - } - pto->fPingQueued = false; - pto->nPingUsecStart = GetTimeMicros(); - if (pto->nVersion > BIP0031_VERSION) { - pto->nPingNonceSent = nonce; - pto->PushMessage("ping", nonce); - } - else { - // Peer is too old to support ping command with nonce, pong will never arrive. - pto->nPingNonceSent = 0; - pto->PushMessage("ping"); - } - } - - TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() - if (!lockMain) - return true; - - // Address refresh broadcast - static int64_t 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 - AdvertizeLocal(pnode); - } - if (!vNodes.empty()) - 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); - } - - CNodeState& state = *State(pto->GetId()); - if (state.fShouldBan) { - if (pto->fWhitelisted) - LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); - else { - pto->fDisconnect = true; - if (pto->addr.IsLocal()) - LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); - else { - CNode::Ban(pto->addr); - } - } - state.fShouldBan = false; - } - - BOOST_FOREACH(const CBlockReject& reject, state.rejects) - pto->PushMessage("reject", (string) "block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); - state.rejects.clear(); - - // Start block sync - if (pindexBestHeader == NULL) - pindexBestHeader = chainActive.Tip(); - bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. - if (!state.fSyncStarted && !pto->fClient && fFetch /*&& !fImporting*/ && !fReindex) { - // Only actively request headers from a single peer, unless we're close to end of initial download. - if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 6 * 60 * 60) { // NOTE: was "close to today" and 24h in Bitcoin - state.fSyncStarted = true; - nSyncStarted++; - //CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; - //LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - //pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0)); - pto->PushMessage("getblocks", chainActive.GetLocator(chainActive.Tip()), 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()*/) { - g_signals.Broadcast(); - } - - // - // 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); - - 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); - - // Detect whether we're stalling - int64_t nNow = GetTimeMicros(); - if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { - // Stalling only triggers when the block download window cannot move. During normal steady state, - // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection - // should only happen during initial block download. - LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); - pto->fDisconnect = true; - } - // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval - // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to - // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link - // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes - // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { - LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); - pto->fDisconnect = true; - } - - // - // Message: getdata (blocks) - // - vector vGetData; - if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vector vToDownload; - NodeId staller = -1; - FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); - BOOST_FOREACH(CBlockIndex* pindex, vToDownload) { - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); - LogPrintf("Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); - } - if (state.nBlocksInFlight == 0 && staller != -1) { - if (State(staller)->nStallingSince == 0) { - State(staller)->nStallingSince = nNow; - LogPrint("net", "Stall started peer=%d\n", staller); - } - } - } - - // - // Message: getdata (non-blocks) - // - while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { - const CInv& inv = (*pto->mapAskFor.begin()).second; - if (!AlreadyHave(inv)) { - if (fDebug) - LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); - 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; + { + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; + + // + // Message: ping + // + bool pingSend = false; + if (pto->fPingQueued) { + // RPC ping request by user + pingSend = true; + } + if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { + // Ping automatically sent as a latency probe & keepalive. + pingSend = true; + } + if (pingSend) { + uint64_t nonce = 0; + while (nonce == 0) { + GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); + } + pto->fPingQueued = false; + pto->nPingUsecStart = GetTimeMicros(); + if (pto->nVersion > BIP0031_VERSION) { + pto->nPingNonceSent = nonce; + pto->PushMessage("ping", nonce); + } else { + // Peer is too old to support ping command with nonce, pong will never arrive. + pto->nPingNonceSent = 0; + pto->PushMessage("ping"); + } + } + + TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() + if (!lockMain) + return true; + + // Address refresh broadcast + static int64_t 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 + AdvertizeLocal(pnode); + } + if (!vNodes.empty()) + 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); + } + + CNodeState& state = *State(pto->GetId()); + if (state.fShouldBan) { + if (pto->fWhitelisted) + LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); + else { + pto->fDisconnect = true; + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); + else { + CNode::Ban(pto->addr); + } + } + state.fShouldBan = false; + } + + BOOST_FOREACH (const CBlockReject& reject, state.rejects) + pto->PushMessage("reject", (string) "block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); + state.rejects.clear(); + + // Start block sync + if (pindexBestHeader == NULL) + pindexBestHeader = chainActive.Tip(); + bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. + if (!state.fSyncStarted && !pto->fClient && fFetch /*&& !fImporting*/ && !fReindex) { + // Only actively request headers from a single peer, unless we're close to end of initial download. + if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 6 * 60 * 60) { // NOTE: was "close to today" and 24h in Bitcoin + state.fSyncStarted = true; + nSyncStarted++; + //CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; + //LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); + //pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0)); + pto->PushMessage("getblocks", chainActive.GetLocator(chainActive.Tip()), 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()*/) { + g_signals.Broadcast(); + } + + // + // 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); + + 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); + + // Detect whether we're stalling + int64_t nNow = GetTimeMicros(); + if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { + // Stalling only triggers when the block download window cannot move. During normal steady state, + // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection + // should only happen during initial block download. + LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); + pto->fDisconnect = true; + } + // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval + // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to + // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link + // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes + // to unreasonably increase our timeout. + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { + LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); + pto->fDisconnect = true; + } + + // + // Message: getdata (blocks) + // + vector vGetData; + if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + vector vToDownload; + NodeId staller = -1; + FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); + BOOST_FOREACH (CBlockIndex* pindex, vToDownload) { + vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); + LogPrintf("Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), + pindex->nHeight, pto->id); + } + if (state.nBlocksInFlight == 0 && staller != -1) { + if (State(staller)->nStallingSince == 0) { + State(staller)->nStallingSince = nNow; + LogPrint("net", "Stall started peer=%d\n", staller); + } + } + } + + // + // Message: getdata (non-blocks) + // + while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { + const CInv& inv = (*pto->mapAskFor.begin()).second; + if (!AlreadyHave(inv)) { + if (fDebug) + LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); + 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; } bool CBlockUndo::WriteToDisk(CDiskBlockPos& pos, const uint256& hashBlock) { - // Open history file to append - CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("CBlockUndo::WriteToDisk : OpenUndoFile failed"); + // Open history file to append + CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("CBlockUndo::WriteToDisk : OpenUndoFile failed"); - // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); - fileout << FLATDATA(Params().MessageStart()) << nSize; + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(Params().MessageStart()) << nSize; - // Write undo data - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("CBlockUndo::WriteToDisk : ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << *this; + // Write undo data + long fileOutPos = ftell(fileout.Get()); + 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(); + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + fileout << hasher.GetHash(); - return true; + return true; } bool CBlockUndo::ReadFromDisk(const CDiskBlockPos& pos, const uint256& hashBlock) { - // Open history file to read - CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - 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 - %s", __func__, e.what()); - } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << *this; - if (hashChecksum != hasher.GetHash()) - return error("CBlockUndo::ReadFromDisk : Checksum mismatch"); + // Open history file to read + CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("CBlockUndo::ReadFromDisk : OpenBlockFile failed"); - return true; + // Read block + uint256 hashChecksum; + try { + filein >> *this; + filein >> hashChecksum; + } catch (std::exception& e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + if (hashChecksum != hasher.GetHash()) + return error("CBlockUndo::ReadFromDisk : Checksum mismatch"); + + return true; } std::string CBlockFileInfo::ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); } class CMainCleanup { public: - CMainCleanup() {} - ~CMainCleanup() - { - // block headers - BlockMap::iterator it1 = mapBlockIndex.begin(); - for (; it1 != mapBlockIndex.end(); it1++) - delete (*it1).second; - mapBlockIndex.clear(); - - // orphan transactions - mapOrphanTransactions.clear(); - mapOrphanTransactionsByPrev.clear(); - } -} instance_of_cmaincleanup; + CMainCleanup() {} + ~CMainCleanup() + { + // block headers + BlockMap::iterator it1 = mapBlockIndex.begin(); + for (; it1 != mapBlockIndex.end(); it1++) + delete (*it1).second; + mapBlockIndex.clear(); + + // orphan transactions + mapOrphanTransactions.clear(); + mapOrphanTransactionsByPrev.clear(); + } +} instance_of_cmaincleanup; \ No newline at end of file diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index aab219a..4b96849 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -6,8 +6,8 @@ 0 0 - 1146 - 806 + 960 + 615 @@ -19,7 +19,7 @@ Form - + @@ -37,136 +37,10 @@ - - - + + + - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - 0 - - - - - - - - - - - - - 75 - true - - - - Recent transactions - - - - - - - WhatsThisCursor - - - The displayed information may be out of date. Your wallet automatically synchronizes with the CCBC network after a connection is established, but this process has not completed yet. - - - QLabel { color: red; } - - - (out of sync) - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 75 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 75 - - - - - - - - QListView { background: transparent; } - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractItemView::NoSelection - - - - - - - - - - - QFrame::StyledPanel @@ -180,10 +54,13 @@ Qt::Vertical + + QSizePolicy::Fixed + 20 - 40 + 75 @@ -242,8 +119,8 @@ 12 - - + + 75 @@ -254,7 +131,7 @@ IBeamCursor - Current total balance in watch-only addresses + Unconfirmed transactions to watch-only addresses 0.000 000 00 CCBC @@ -267,8 +144,8 @@ - - + + 75 @@ -279,7 +156,7 @@ IBeamCursor - Your current spendable balance + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance 0.000 000 00 CCBC @@ -292,8 +169,8 @@ - - + + 75 @@ -304,7 +181,7 @@ IBeamCursor - Your current balance in watch-only addresses + Staked or masternode rewards in watch-only addresses that has not yet matured 0.000 000 00 CCBC @@ -317,25 +194,41 @@ - - - - Pending: + + + + Qt::Horizontal - - - - Spendable: + + + + + 0 + 0 + - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 140 + 0 + + + + Qt::Horizontal - - + + + + Total: + + + + + 75 @@ -346,7 +239,7 @@ IBeamCursor - Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + Staked or masternode rewards that has not yet matured 0.000 000 00 CCBC @@ -359,8 +252,28 @@ - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Immature: + + + + + 75 @@ -371,7 +284,7 @@ IBeamCursor - Staked or masternode rewards in watch-only addresses that has not yet matured + Your current total balance 0.000 000 00 CCBC @@ -384,8 +297,8 @@ - - + + 75 @@ -396,7 +309,7 @@ IBeamCursor - Unconfirmed transactions to watch-only addresses + Current total balance in watch-only addresses 0.000 000 00 CCBC @@ -409,15 +322,25 @@ - - - - Qt::Horizontal + + + + Watch-only: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + Available: + + + + + 75 @@ -428,7 +351,7 @@ IBeamCursor - Staked or masternode rewards that has not yet matured + Your current spendable balance 0.000 000 00 CCBC @@ -441,72 +364,19 @@ - - - - - 0 - 0 - + + + + + 75 + true + - - - 140 - 0 - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Total: - - - - - - - Immature: - - - - - - - Available: - - - - - - - - 75 - true - - - - IBeamCursor + + IBeamCursor - Your current total balance + Your current balance in watch-only addresses 0.000 000 00 CCBC @@ -519,304 +389,1112 @@ - - + + - Watch-only: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Pending: - - - - - 0 - 0 - - - - - 140 - 0 - - - - Qt::Horizontal + + + + Spendable: - - - - - - Qt::Horizontal + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 350 + + + + Qt::LeftToRight + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 251 + 17 + 1 + 1 + + + + + + + + + 0 + 0 + 0 + + + + + + + 239 + 238 + 238 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 246 + 246 + + + + + + + 119 + 119 + 119 + + + + + + + 159 + 159 + 159 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 239 + 238 + 238 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 246 + 246 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 239 + 238 + 238 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 246 + 246 + + + + + + + 119 + 119 + 119 + + + + + + + 159 + 159 + 159 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 239 + 238 + 238 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 246 + 246 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 119 + 119 + 119 + + + + + + + 239 + 238 + 238 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 246 + 246 + + + + + + + 119 + 119 + 119 + + + + + + + 159 + 159 + 159 + + + + + + + 119 + 119 + 119 + + + + + + + 255 + 255 + 255 + + + + + + + 119 + 119 + 119 + + + + + + + 239 + 238 + 238 + + + + + + + 239 + 238 + 238 + + + + + + + 0 + 0 + 0 + + + + + + + 239 + 238 + 238 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + Qt::NoFocus + + + true + + + + + + true + + + + + + 7 + 10 + 451 + 1 + + + + Qt::Horizontal + + + + + + 0 + 150 + 465 + 201 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 16777215 + 20 + + + + + 75 + true + + + + Combined Balances (including unconfirmed and immature coins) + + + <html><head/><body><p>Combined Balances</p></body></html> + + + + + + + + + 12 + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 140 + 0 + + + + Qt::Horizontal + + + + + + + + 75 + true + + + + IBeamCursor + + + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + + + 0.000 000 00 CCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + IBeamCursor + + + Total Balance, including unconfirmed and immature coins. + + + 0.000 000 00 CCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Total: + + + + + + + CCBC: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your current spendable balance + + + 0.000 000 00 CCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + zCCBC: + + + + + + + + Current percentage of zCCBC. + If AutoMint is enabled this percentage will settle around the configured AutoMint percentage (default = 10%) + + + + 0 % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Locked CCBC or Masternode collaterals. These are excluded from zCCBC minting. + + + 0.000 000 00 CCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Locked CCBC or Masternode collaterals. These are excluded from zCCBC minting. + + + Locked: + + + + + + + Unlocked CCBCs. These can be used for zCCBC minting. + + + Unlocked: + + + + + + + Unlocked CCBCs. These can be used for zCCBC minting. + + + 0.000 000 00 CCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0 % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + 465 + 171 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 16777215 + 20 + + + + + 75 + true + + + + zCCBC Balance + + + + + + + + + 12 + + + + + + Mature: more than 20 confirmation and more than 1 mints of the same denomination after it was minted. + These zCCBC are spendable. + + + + Mature: + + + + + + + Qt::Horizontal + + + + + + + + 75 + true + + + + IBeamCursor + + + All available zCCBC, unconfirmed and immature zCCBC included. + + + 0.000 000 00 zCCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + 140 + 0 + + + + Qt::Horizontal + + + + + + + All available zCCBC, unconfirmed and immature zCCBC included. + + + Total: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + + Mature: more than 20 confirmation and more than 1 mints of the same denomination after it was minted. + These zCCBC are spendable. + + + + 0.000 000 00 zCCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mints of the same denomination after it was minted + + + + Unconfirmed: + + + + + + + + 75 + true + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mints of the same denomination after it was minted + + + + 0.000 000 00 zCCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mints of the same denomination after it was minted + + + + Immature: + + + + + + + + 75 + true + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mints of the same denomination after it was minted + + + + 0.000 000 00 zCCBC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + - - - - - - 75 - true - - - - Masternode Info - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 75 + + + - - - 12 - - - + + + 75 true - - IBeamCursor - - - Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Collateral: - - - - - - Days Till ROI: + Recent transactions - - - - - 75 - true - - + + - IBeamCursor + WhatsThisCursor - Your current total balance - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - 75 - true - - - - IBeamCursor + The displayed information may be out of date. Your wallet automatically synchronizes with the CCBC network after a connection is established, but this process has not completed yet. - - Staked or masternode rewards that has not yet matured + + QLabel { color: red; } - 0 + (out of sync) Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - MN Online: - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 75 - true - - - - IBeamCursor - - - Your current spendable balance - - - 25,000 CCBC - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Daily Rewards: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 140 - 0 - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - - - - - - 75 - true - - - - Network Information - - - - - + + Qt::Horizontal @@ -831,229 +1509,49 @@ - - - 12 + + + QListView { background: transparent; } - - - - - 75 - true - - - - IBeamCursor - - - Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - 75 - true - - - - IBeamCursor - - - Your current total balance - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Coin Supply: - - - - - - - - 75 - true - - - - IBeamCursor - - - Staked or masternode rewards that has not yet matured - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Current Block: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Network Hash: - - - - - - - Block Reward: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 75 - true - - - - IBeamCursor - - - Your current spendable balance - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - 0 - 0 - - - - - 140 - 0 - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoSelection + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + + - - - + diff --git a/src/qt/forms/privacydialog.ui b/src/qt/forms/privacydialog.ui index ba89cca..d3e34cc 100644 --- a/src/qt/forms/privacydialog.ui +++ b/src/qt/forms/privacydialog.ui @@ -673,21 +673,69 @@ + + + + + 460 + 0 + + + + + 16777215 + 48 + + + + + 14 + 75 + true + + + + CCBC which were anonymized via Zerocin Protocol + + + true + + + Zerocoin CCBC (zCCBC) + + + Qt::AlignCenter + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + - - + + - + - Denominations with value 10: + Denominations with value 50: - Denom. with value 10: + Denom. with value 50: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -695,7 +743,7 @@ - + Qt::Horizontal @@ -708,7 +756,93 @@ - + + + + 40 + 0 + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mint of the same denomination after it was minted + + + + 0 x + + + Qt::RichText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Denom. 1: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 0 zCCBC + + + + + + + + + + + Denominations with value 5: + + + Denom. with value 5: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + 40 @@ -734,6 +868,118 @@ + + + + + + + Show the current status of automatic zCCBC minting. + + To change the status (restart required): + - enable: add 'enablezeromint=1' to pivx.conf + - disable: add 'enablezeromint=0' to pivx.conf + + To change the percentage (no restart required): + - menu Settings->Options->Percentage of autominted zCCBC + + + + + AutoMint Status + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + Automint Is Disabled + + + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + + + 75 + true + + + + Global Supply: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + 0 zCCBC + + + + + @@ -906,6 +1152,25 @@ + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + @@ -961,25 +1226,6 @@ - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - @@ -1121,37 +1367,6 @@ - - - - - - Denom. 10: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 0 zCCBC - - - - - @@ -1168,17 +1383,17 @@ - - + + - + - Denom. 50: + Denom. 10: - + Qt::Horizontal @@ -1191,7 +1406,7 @@ - + 0 zCCBC @@ -1199,127 +1414,17 @@ - - - - - - Denominations with value 1: - - - Denom. with value 1: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 40 - 0 - - - - - Unconfirmed: less than 20 confirmations - Immature: confirmed, but less than 1 mint of the same denomination after it was minted - - - - 0 x - - - Qt::RichText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - Denominations with value 50: - - - Denom. with value 50: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 40 - 0 - - - - - Unconfirmed: less than 20 confirmations - Immature: confirmed, but less than 1 mint of the same denomination after it was minted - - - - 0 x - - - Qt::RichText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + + - + - Denom. 1: + Denom. 50: - + Qt::Horizontal @@ -1332,7 +1437,7 @@ - + 0 zCCBC @@ -1340,85 +1445,23 @@ - - - - - - Denominations with value 5: - - - Denom. with value 5: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 40 - 0 - - - - - Unconfirmed: less than 20 confirmations - Immature: confirmed, but less than 1 mint of the same denomination after it was minted - - - - 0 x - - - Qt::RichText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - Show the current status of automatic zCCBC minting. - - To change the status (restart required): - - enable: add 'enablezeromint=1' to ccbc.conf - - disable: add 'enablezeromint=0' to ccbc.conf - - To change the percentage (no restart required): - - menu Settings->Options->Percentage of autominted zCCBC - - + + + + + + Denominations with value 1: - AutoMint Status + Denom. with value 1: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - + Qt::Horizontal @@ -1431,56 +1474,49 @@ - - - - 75 - true - + + + + 40 + 0 + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mint of the same denomination after it was minted + - Automint Is Disabled + 0 x + + + Qt::RichText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - + + - - - - 75 - true - + + + Denominations with value 10: - Global Supply: + Denom. with value 10: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - + Qt::Horizontal @@ -1493,15 +1529,27 @@ - - - - 75 - true - + + + + 40 + 0 + + + + + Unconfirmed: less than 20 confirmations + Immature: confirmed, but less than 1 mint of the same denomination after it was minted + - 0 zCCBC + 0 x + + + Qt::RichText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -1728,17 +1776,17 @@ - - + + - + - Denom. 5000: + Denom. 500: - + Qt::Horizontal @@ -1751,7 +1799,7 @@ - + 0 zCCBC @@ -1759,17 +1807,17 @@ - - + + - + - Denom. 500: + Denom. 5000: - + Qt::Horizontal @@ -1782,7 +1830,7 @@ - + 0 zCCBC @@ -1790,6 +1838,25 @@ + + + + + 16777215 + 20 + + + + + 75 + true + + + + Zerocoin Transactional Information + + + @@ -1821,42 +1888,10 @@ - - - - - 16777215 - 20 - - - - - 75 - true - - - - Zerocoin Transactional Information - - - - - - - - 1 - 16777215 - - - - Qt::Vertical - - - @@ -2661,8 +2696,46 @@ + + + + + 1 + 16777215 + + + + Qt::Vertical + + + + + + + false + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + 3 + + + @@ -2700,57 +2773,8 @@ - - - - - 460 - 0 - - - - - 16777215 - 48 - - - - - 14 - 75 - true - - - - CCBC which were anonymized via Zerocin Protocol - - - true - - - Zerocoin CCBC (zCCBC) - - - Qt::AlignCenter - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - zCCBCLabel - line_2 + labelCoinControlChangeLabel dummyHideWidget WarningLabel labelCoinControlAfterFee diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index abe370a..1c9769c 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -8,30 +8,22 @@ #include "overviewpage.h" #include "ui_overviewpage.h" -#include "addressbookpage.h" -#include "amount.h" #include "bitcoinunits.h" -#include "chainparams.h" #include "clientmodel.h" #include "guiconstants.h" #include "guiutil.h" #include "init.h" -#include "main.h" -#include "masternodeman.h" #include "obfuscation.h" #include "obfuscationconfig.h" #include "optionsmodel.h" -#include "rpcblockchain.cpp" #include "transactionfilterproxy.h" #include "transactiontablemodel.h" #include "walletmodel.h" #include -#include #include #include #include -#include #define DECORATION_SIZE 48 #define ICON_OFFSET 16 @@ -41,485 +33,310 @@ extern CWallet* pwalletMain; class TxViewDelegate : public QAbstractItemDelegate { - Q_OBJECT + Q_OBJECT public: - TxViewDelegate() : QAbstractItemDelegate(), unit(BitcoinUnits::CCBC) - { - } - - inline void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const - { - painter->save(); - - QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); - QRect mainRect = option.rect; - mainRect.moveLeft(ICON_OFFSET); - QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE)); - int xspace = DECORATION_SIZE + 8; - int ypad = 6; - int halfheight = (mainRect.height() - 2 * ypad) / 2; - QRect amountRect(mainRect.left() + xspace, mainRect.top() + ypad, mainRect.width() - xspace - ICON_OFFSET, halfheight); - QRect addressRect(mainRect.left() + xspace, mainRect.top() + ypad + halfheight, mainRect.width() - xspace, halfheight); - icon.paint(painter, decorationRect); - - QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); - QString address = index.data(Qt::DisplayRole).toString(); - qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong(); - bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool(); - QVariant value = index.data(Qt::ForegroundRole); - QColor foreground = COLOR_BLACK; - if (value.canConvert()) { - QBrush brush = qvariant_cast(value); - foreground = brush.color(); - } - - painter->setPen(foreground); - QRect boundingRect; - painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect); - - if (index.data(TransactionTableModel::WatchonlyRole).toBool()) { - QIcon iconWatchonly = qvariant_cast(index.data(TransactionTableModel::WatchonlyDecorationRole)); - QRect watchonlyRect(boundingRect.right() + 5, mainRect.top() + ypad + halfheight, 16, halfheight); - iconWatchonly.paint(painter, watchonlyRect); - } - - if (amount < 0) { - foreground = COLOR_NEGATIVE; - } - else if (!confirmed) { - foreground = COLOR_UNCONFIRMED; - } - else { - foreground = COLOR_BLACK; - } - painter->setPen(foreground); - QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways); - if (!confirmed) { - amountText = QString("[") + amountText + QString("]"); - } - painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter, amountText); - - painter->setPen(COLOR_BLACK); - painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date)); - - painter->restore(); - } - - inline QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const - { - return QSize(DECORATION_SIZE, DECORATION_SIZE); - } - - int unit; + TxViewDelegate() : QAbstractItemDelegate(), unit(BitcoinUnits::CCBC) + { + } + + inline void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const + { + painter->save(); + + QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); + QRect mainRect = option.rect; + mainRect.moveLeft(ICON_OFFSET); + QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE)); + int xspace = DECORATION_SIZE + 8; + int ypad = 6; + int halfheight = (mainRect.height() - 2 * ypad) / 2; + QRect amountRect(mainRect.left() + xspace, mainRect.top() + ypad, mainRect.width() - xspace - ICON_OFFSET, halfheight); + QRect addressRect(mainRect.left() + xspace, mainRect.top() + ypad + halfheight, mainRect.width() - xspace, halfheight); + icon.paint(painter, decorationRect); + + QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); + QString address = index.data(Qt::DisplayRole).toString(); + qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong(); + bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool(); + QVariant value = index.data(Qt::ForegroundRole); + QColor foreground = COLOR_BLACK; + if (value.canConvert()) { + QBrush brush = qvariant_cast(value); + foreground = brush.color(); + } + + painter->setPen(foreground); + QRect boundingRect; + painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect); + + if (index.data(TransactionTableModel::WatchonlyRole).toBool()) { + QIcon iconWatchonly = qvariant_cast(index.data(TransactionTableModel::WatchonlyDecorationRole)); + QRect watchonlyRect(boundingRect.right() + 5, mainRect.top() + ypad + halfheight, 16, halfheight); + iconWatchonly.paint(painter, watchonlyRect); + } + + if (amount < 0) { + foreground = COLOR_NEGATIVE; + } else if (!confirmed) { + foreground = COLOR_UNCONFIRMED; + } else { + foreground = COLOR_BLACK; + } + painter->setPen(foreground); + QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways); + if (!confirmed) { + amountText = QString("[") + amountText + QString("]"); + } + painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter, amountText); + + painter->setPen(COLOR_BLACK); + painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date)); + + painter->restore(); + } + + inline QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const + { + return QSize(DECORATION_SIZE, DECORATION_SIZE); + } + + int unit; }; #include "overviewpage.moc" OverviewPage::OverviewPage(QWidget* parent) : QWidget(parent), -ui(new Ui::OverviewPage), -clientModel(0), -walletModel(0), -currentBalance(-1), -currentUnconfirmedBalance(-1), -currentImmatureBalance(-1), -//currentZerocoinBalance(-1), -//currentUnconfirmedZerocoinBalance(-1), -//currentimmatureZerocoinBalance(-1), -currentWatchOnlyBalance(-1), -currentWatchUnconfBalance(-1), -currentWatchImmatureBalance(-1), -txdelegate(new TxViewDelegate()), -filter(0) + ui(new Ui::OverviewPage), + clientModel(0), + walletModel(0), + currentBalance(-1), + currentUnconfirmedBalance(-1), + currentImmatureBalance(-1), + currentZerocoinBalance(-1), + currentUnconfirmedZerocoinBalance(-1), + currentimmatureZerocoinBalance(-1), + currentWatchOnlyBalance(-1), + currentWatchUnconfBalance(-1), + currentWatchImmatureBalance(-1), + txdelegate(new TxViewDelegate()), + filter(0) { - nDisplayUnit = 0; // just make sure it's not unitialized - ui->setupUi(this); - /* - ui->pushButton_Website->setIcon(QIcon(":/icons/website")); - ui->pushButton_Discord->setIcon(QIcon(":/icons/discord")); - ui->pushButton_Github->setIcon(QIcon(":/icons/github")); - ui->pushButton_Twitter->setIcon(QIcon(":/icons/twitter")); - ui->pushButton_Explorer->setIcon(QIcon(":/icons/explorer")); - */ - - // Recent transactions - ui->listTransactions->setItemDelegate(txdelegate); - ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); - ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); - ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); - - connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); - - - // init "out of sync" warning labels - ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); - ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); - - //information block update - timerinfo_mn = new QTimer(this); - connect(timerinfo_mn, SIGNAL(timeout()), this, SLOT(updateMasternodeInfo())); - timerinfo_mn->start(1000); - - timerinfo_blockchain = new QTimer(this); - connect(timerinfo_blockchain, SIGNAL(timeout()), this, SLOT(updatBlockChainInfo())); - timerinfo_blockchain->start(1000); //30sec - - // start with displaying the "out of sync" warnings - showOutOfSyncWarning(true); + nDisplayUnit = 0; // just make sure it's not unitialized + ui->setupUi(this); + + // Recent transactions + ui->listTransactions->setItemDelegate(txdelegate); + ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); + ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); + ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); + + connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); + + + // init "out of sync" warning labels + ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); + ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); + + // start with displaying the "out of sync" warnings + showOutOfSyncWarning(true); } void OverviewPage::handleTransactionClicked(const QModelIndex& index) { - if (filter) - emit transactionClicked(filter->mapToSource(index)); + if (filter) + emit transactionClicked(filter->mapToSource(index)); } OverviewPage::~OverviewPage() { - delete ui; + delete ui; } -/* void OverviewPage::getPercentage(CAmount nUnlockedBalance, CAmount nZerocoinBalance, QString& sCCBCPercentage, QString& szCCBCPercentage) { -int nPrecision = 2; -double dzPercentage = 0.0; -if (nZerocoinBalance <= 0){ -dzPercentage = 0.0; -} -else{ -if (nUnlockedBalance <= 0){ -dzPercentage = 100.0; -} -else{ -dzPercentage = 100.0 * (double)(nZerocoinBalance / (double)(nZerocoinBalance + nUnlockedBalance)); -} -} -double dPercentage = 100.0 - dzPercentage; - -szCCBCPercentage = "(" + QLocale(QLocale::system()).toString(dzPercentage, 'f', nPrecision) + " %)"; -sCCBCPercentage = "(" + QLocale(QLocale::system()).toString(dPercentage, 'f', nPrecision) + " %)"; - + int nPrecision = 2; + double dzPercentage = 0.0; + + if (nZerocoinBalance <= 0) { + dzPercentage = 0.0; + } else { + if (nUnlockedBalance <= 0) { + dzPercentage = 100.0; + } else { + dzPercentage = 100.0 * (double)(nZerocoinBalance / (double)(nZerocoinBalance + nUnlockedBalance)); + } + } + + double dPercentage = 100.0 - dzPercentage; + + szCCBCPercentage = "(" + QLocale(QLocale::system()).toString(dzPercentage, 'f', nPrecision) + " %)"; + sCCBCPercentage = "(" + QLocale(QLocale::system()).toString(dPercentage, 'f', nPrecision) + " %)"; } -*/ -void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, - //const CAmount& zerocoinBalance, const CAmount& unconfirmedZerocoinBalance, const CAmount& immatureZerocoinBalance, - const CAmount& watchOnlyBalance, - const CAmount& watchUnconfBalance, - const CAmount& watchImmatureBalance) +void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& zerocoinBalance, const CAmount& unconfirmedZerocoinBalance, const CAmount& immatureZerocoinBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance) { - currentBalance = balance; - currentUnconfirmedBalance = unconfirmedBalance; - currentImmatureBalance = immatureBalance; - //currentZerocoinBalance = zerocoinBalance; - //currentUnconfirmedZerocoinBalance = unconfirmedZerocoinBalance; - //currentimmatureZerocoinBalance = immatureZerocoinBalance; - currentWatchOnlyBalance = watchOnlyBalance; - currentWatchUnconfBalance = watchUnconfBalance; - currentWatchImmatureBalance = watchImmatureBalance; - - // CCBC labels - ui->labelBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, balance - immatureBalance, false, BitcoinUnits::separatorAlways)); - //ui->labelzBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, zerocoinBalance, false, BitcoinUnits::separatorAlways)); - ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, unconfirmedBalance, false, BitcoinUnits::separatorAlways)); - ui->labelImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, immatureBalance, false, BitcoinUnits::separatorAlways)); - ui->labelTotal->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, balance + unconfirmedBalance, false, BitcoinUnits::separatorAlways)); - - // Watchonly labels - ui->labelWatchAvailable->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchOnlyBalance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchPending->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchUnconfBalance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchImmatureBalance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchTotal->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways)); - - - //zCCBC labels - /* - QString szPercentage = ""; - QString sPercentage = ""; - CAmount nLockedBalance = 0; - if (pwalletMain) { - nLockedBalance = pwalletMain->GetLockedCoins(); - } - ui->labelLockedBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nLockedBalance, false, BitcoinUnits::separatorAlways)); - CAmount nTotalBalance = balance + unconfirmedBalance; - CAmount nUnlockedBalance = nTotalBalance - nLockedBalance; - CAmount matureZerocoinBalance = zerocoinBalance - immatureZerocoinBalance; - getPercentage(nUnlockedBalance, zerocoinBalance, sPercentage, szPercentage); - ui->labelBalancez->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nTotalBalance, false, BitcoinUnits::separatorAlways)); - ui->labelzBalancez->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, zerocoinBalance, false, BitcoinUnits::separatorAlways)); - ui->labelzBalanceImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, immatureZerocoinBalance, false, BitcoinUnits::separatorAlways)); - ui->labelzBalanceUnconfirmed->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, unconfirmedZerocoinBalance, false, BitcoinUnits::separatorAlways)); - ui->labelzBalanceMature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, matureZerocoinBalance, false, BitcoinUnits::separatorAlways)); - ui->labelTotalz->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nTotalBalance + zerocoinBalance, false, BitcoinUnits::separatorAlways)); - ui->labelUnLockedBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nUnlockedBalance, false, BitcoinUnits::separatorAlways)); - ui->labelCCBCPercent->setText(sPercentage); - ui->labelzCCBCPercent->setText(szPercentage); - // Adjust bubble-help according to AutoMint settings - QString automintHelp = tr("Current percentage of zCCBC.\nIf AutoMint is enabled this percentage will settle around the configured AutoMint percentage (default = 10%).\n"); - bool fEnableZeromint = GetBoolArg("-enablezeromint", false); - int nZeromintPercentage = GetArg("-zeromintpercentage", 10); - if (fEnableZeromint) { - automintHelp += tr("AutoMint is currently enabled and set to ") + QString::number(nZeromintPercentage) + "%.\n"; - automintHelp += tr("To disable AutoMint delete set 'enablezeromint=1' to 'enablezeromint=0' in ccbc.conf."); - } - else { - automintHelp += tr("AutoMint is currently disabled.\nTo enable AutoMint add 'enablezeromint=1' in ccbc.conf"); - } - ui->labelzCCBCPercent->setToolTip(automintHelp); - */ - - //only show immature (newly mined) balance if it's non-zero, so as not to complicate things - //for the non-mining users - bool showImmature = immatureBalance != 0; - bool showWatchOnlyImmature = watchImmatureBalance != 0; - - // for symmetry reasons also show immature label when the watch-only one is shown - ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); - ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); - ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance - - static int cachedTxLocks = 0; - - if (cachedTxLocks != nCompleteTXLocks) { - cachedTxLocks = nCompleteTXLocks; - ui->listTransactions->update(); - } + currentBalance = balance; + currentUnconfirmedBalance = unconfirmedBalance; + currentImmatureBalance = immatureBalance; + currentZerocoinBalance = zerocoinBalance; + currentUnconfirmedZerocoinBalance = unconfirmedZerocoinBalance; + currentimmatureZerocoinBalance = immatureZerocoinBalance; + currentWatchOnlyBalance = watchOnlyBalance; + currentWatchUnconfBalance = watchUnconfBalance; + currentWatchImmatureBalance = watchImmatureBalance; + + // CCBC labels + ui->labelBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, balance - immatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelzBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, zerocoinBalance, false, BitcoinUnits::separatorAlways)); + ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, unconfirmedBalance, false, BitcoinUnits::separatorAlways)); + ui->labelImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, immatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelTotal->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, balance + unconfirmedBalance, false, BitcoinUnits::separatorAlways)); + + // Watchonly labels + ui->labelWatchAvailable->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchOnlyBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchPending->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchUnconfBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchImmatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchTotal->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways)); + + // zCCBC labels + QString szPercentage = ""; + QString sPercentage = ""; + CAmount nLockedBalance = 0; + if (pwalletMain) { + nLockedBalance = pwalletMain->GetLockedCoins(); + } + ui->labelLockedBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nLockedBalance, false, BitcoinUnits::separatorAlways)); + + CAmount nTotalBalance = balance + unconfirmedBalance; + CAmount nUnlockedBalance = nTotalBalance - nLockedBalance; + CAmount matureZerocoinBalance = zerocoinBalance - immatureZerocoinBalance; + getPercentage(nUnlockedBalance, zerocoinBalance, sPercentage, szPercentage); + + ui->labelBalancez->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nTotalBalance, false, BitcoinUnits::separatorAlways)); + ui->labelzBalancez->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, zerocoinBalance, false, BitcoinUnits::separatorAlways)); + ui->labelzBalanceImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, immatureZerocoinBalance, false, BitcoinUnits::separatorAlways)); + ui->labelzBalanceUnconfirmed->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, unconfirmedZerocoinBalance, false, BitcoinUnits::separatorAlways)); + ui->labelzBalanceMature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, matureZerocoinBalance, false, BitcoinUnits::separatorAlways)); + ui->labelTotalz->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nTotalBalance + zerocoinBalance, false, BitcoinUnits::separatorAlways)); + ui->labelUnLockedBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, nUnlockedBalance, false, BitcoinUnits::separatorAlways)); + ui->labelCCBCPercent->setText(sPercentage); + ui->labelzCCBCPercent->setText(szPercentage); + + // Adjust bubble-help according to AutoMint settings + QString automintHelp = tr("Current percentage of zCCBC.\nIf AutoMint is enabled this percentage will settle around the configured AutoMint percentage (default = 10%).\n"); + bool fEnableZeromint = GetBoolArg("-enablezeromint", false); + int nZeromintPercentage = GetArg("-zeromintpercentage", 10); + if (fEnableZeromint) { + automintHelp += tr("AutoMint is currently enabled and set to ") + QString::number(nZeromintPercentage) + "%.\n"; + automintHelp += tr("To disable AutoMint delete set 'enablezeromint=1' to 'enablezeromint=0' in ccbc.conf."); + } else { + automintHelp += tr("AutoMint is currently disabled.\nTo enable AutoMint add 'enablezeromint=1' in ccbc.conf"); + } + ui->labelzCCBCPercent->setToolTip(automintHelp); + + // only show immature (newly mined) balance if it's non-zero, so as not to complicate things + // for the non-mining users + bool showImmature = immatureBalance != 0; + bool showWatchOnlyImmature = watchImmatureBalance != 0; + + // for symmetry reasons also show immature label when the watch-only one is shown + ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); + ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); + ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance + + static int cachedTxLocks = 0; + + if (cachedTxLocks != nCompleteTXLocks) { + cachedTxLocks = nCompleteTXLocks; + ui->listTransactions->update(); + } } // show/hide watch-only labels - void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) { - ui->labelSpendable->setVisible(showWatchOnly); // show spendable label (only when watch-only is active) - ui->labelWatchonly->setVisible(showWatchOnly); // show watch-only label - ui->lineWatchBalance->setVisible(showWatchOnly); // show watch-only balance separator line - ui->labelWatchAvailable->setVisible(showWatchOnly); // show watch-only available balance - ui->labelWatchPending->setVisible(showWatchOnly); // show watch-only pending balance - ui->labelWatchTotal->setVisible(showWatchOnly); // show watch-only total balance - - if (!showWatchOnly) { - ui->labelWatchImmature->hide(); - } - else { - ui->labelBalance->setIndent(20); - ui->labelUnconfirmed->setIndent(20); - ui->labelImmature->setIndent(20); - ui->labelTotal->setIndent(20); - } + ui->labelSpendable->setVisible(showWatchOnly); // show spendable label (only when watch-only is active) + ui->labelWatchonly->setVisible(showWatchOnly); // show watch-only label + ui->lineWatchBalance->setVisible(showWatchOnly); // show watch-only balance separator line + ui->labelWatchAvailable->setVisible(showWatchOnly); // show watch-only available balance + ui->labelWatchPending->setVisible(showWatchOnly); // show watch-only pending balance + ui->labelWatchTotal->setVisible(showWatchOnly); // show watch-only total balance + + if (!showWatchOnly) { + ui->labelWatchImmature->hide(); + } else { + ui->labelBalance->setIndent(20); + ui->labelUnconfirmed->setIndent(20); + ui->labelImmature->setIndent(20); + ui->labelTotal->setIndent(20); + } } - void OverviewPage::setClientModel(ClientModel* model) { - this->clientModel = model; - if (model) { - // Show warning if this is a prerelease version - connect(model, SIGNAL(alertsChanged(QString)), this, SLOT(updateAlerts(QString))); - updateAlerts(model->getStatusBarWarnings()); - } + this->clientModel = model; + if (model) { + // Show warning if this is a prerelease version + connect(model, SIGNAL(alertsChanged(QString)), this, SLOT(updateAlerts(QString))); + updateAlerts(model->getStatusBarWarnings()); + } } void OverviewPage::setWalletModel(WalletModel* model) { - this->walletModel = model; - if (model && model->getOptionsModel()) { - // Set up transaction list - filter = new TransactionFilterProxy(); - filter->setSourceModel(model->getTransactionTableModel()); - filter->setLimit(NUM_ITEMS); - filter->setDynamicSortFilter(true); - filter->setSortRole(Qt::EditRole); - filter->setShowInactive(false); - filter->sort(TransactionTableModel::Date, Qt::DescendingOrder); - - ui->listTransactions->setModel(filter); - ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); - - // Keep up to date with wallet - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), - //setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), - //model->getZerocoinBalance(), model->getUnconfirmedZerocoinBalance(), model->getImmatureZerocoinBalance(), - model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); - //connect(model, SIGNAL(balanceChanged(CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount)), this, - //SLOT(setBalance(CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount))); - connect(model, SIGNAL(balanceChanged(CAmount, CAmount, CAmount, CAmount, CAmount, CAmount)), this, - SLOT(setBalance(CAmount, CAmount, CAmount, CAmount, CAmount, CAmount))); - - - connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - - updateWatchOnlyLabels(model->haveWatchOnly()); - //connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); - } - - // update the display unit, to not use the default ("CCBC") - updateDisplayUnit(); + this->walletModel = model; + if (model && model->getOptionsModel()) { + // Set up transaction list + filter = new TransactionFilterProxy(); + filter->setSourceModel(model->getTransactionTableModel()); + filter->setLimit(NUM_ITEMS); + filter->setDynamicSortFilter(true); + filter->setSortRole(Qt::EditRole); + filter->setShowInactive(false); + filter->sort(TransactionTableModel::Date, Qt::DescendingOrder); + + ui->listTransactions->setModel(filter); + ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); + + // Keep up to date with wallet + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), + model->getZerocoinBalance(), model->getUnconfirmedZerocoinBalance(), model->getImmatureZerocoinBalance(), + model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + connect(model, SIGNAL(balanceChanged(CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount)), this, + SLOT(setBalance(CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount, CAmount))); + + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + updateWatchOnlyLabels(model->haveWatchOnly()); + connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); + } + + // update the display unit, to not use the default ("CCBC") + updateDisplayUnit(); } void OverviewPage::updateDisplayUnit() { - if (walletModel && walletModel->getOptionsModel()) { - nDisplayUnit = walletModel->getOptionsModel()->getDisplayUnit(); - if (currentBalance != -1) - //setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance, currentZerocoinBalance, currentUnconfirmedZerocoinBalance, currentimmatureZerocoinBalance, - setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance, - currentWatchOnlyBalance, currentWatchUnconfBalance, currentWatchImmatureBalance); - - // Update txdelegate->unit with the current unit - txdelegate->unit = nDisplayUnit; - - ui->listTransactions->update(); - } -} - -//All credit goes to the ESB team for developing this. https://github.com/BlockchainFor/ESBC2 -void OverviewPage::updateMasternodeInfo() -{ - if (masternodeSync.IsBlockchainSynced() && masternodeSync.IsSynced()) { - int mn1 = 0; - int mn2 = 0; - int mn3 = 0; - int mn4 = 0; - int totalmn = 0; - std::vector vMasternodes = mnodeman.GetFullMasternodeVector(); - for (auto& mn : vMasternodes) { - switch (mn.nActiveState = true) { - case 1: - mn1++; - break; - case 2: - mn2++; - break; - case 3: - mn3++; - break; - case 4: - mn4++; - break; - } - } - totalmn = mn1 + mn2 + mn3 + mn4; - ui->labelMnTotal_Value->setText(QString::number(totalmn)); - //ui->graphMN->setMaximum(totalmn); - //ui->graphMN->setValue(mn1); - - - // TODO: need a read actual 24h blockcount from chain - int BlockCount24h = 1440; - // update ROI - double BlockReward = GetBlockValue(chainActive.Height()); - double roi1 = (0.72 * BlockReward * BlockCount24h) / mn1 / COIN; - double roi2 = (0.74 * BlockReward * BlockCount24h) / mn1 / COIN; - double roi3 = (0.76 * BlockReward * BlockCount24h) / mn1 / COIN; - double roi4 = (0.78 * BlockReward * BlockCount24h) / mn1 / COIN; - double roi5 = (0.80 * BlockReward * BlockCount24h) / mn1 / COIN; - double roi6 = (0.85 * BlockReward * BlockCount24h) / mn1 / COIN; - double roi7 = (0.90 * BlockReward * BlockCount24h) / mn1 / COIN; - - if (chainActive.Height() <= 91000 && chainActive.Height() > 88000) { //72% - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi1, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi1, 'f', 1).append(" days")); - } - else if (chainActive.Height() <= 94000 && chainActive.Height() > 91000) { //74% - - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi2, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi2, 'f', 1).append(" days")); - } - else if (chainActive.Height() <= 97000 && chainActive.Height() > 94000) { //76% - - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi3, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi3, 'f', 1).append(" days")); - } - else if (chainActive.Height() <= 100000 && chainActive.Height() > 97000) { //78% - - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi4, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi4, 'f', 1).append(" days")); - } - else if (chainActive.Height() <= 125000 && chainActive.Height() > 100000) { //80% - - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi5, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi5, 'f', 1).append(" days")); - } - else if (chainActive.Height() <= 150000 && chainActive.Height() > 125000) { //85% - - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi6, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi6, 'f', 1).append(" days")); - } - else if (chainActive.Height() > 150000) { //90% - - ui->roi->setText(mn1 == 0 ? "-" : QString::number(roi7, 'f', 0).append(" CCBC")); - ui->roi_1->setText(mn1 == 0 ? " " : QString::number(25000 / roi7, 'f', 1).append(" days")); - } - - // update timer - if (timerinfo_mn->interval() == 1000) - timerinfo_mn->setInterval(10000); - } - - // update collateral info - if (chainActive.Height() >= 0) { - ui->label_lcolat->setText("25000 CCBC"); - } -} - -//All credit goes to the ESB team for developing this. https://github.com/BlockchainFor/ESBC2 -void OverviewPage::updatBlockChainInfo() -{ - if (masternodeSync.IsBlockchainSynced()) { - int CurrentBlock = (int)chainActive.Height(); - int64_t netHashRate = chainActive.GetNetworkHashPS(24, CurrentBlock - 1); - double BlockReward = GetBlockValue(chainActive.Height()); - double BlockRewardesbcoin = static_cast(BlockReward / COIN); - double CurrentDiff = GetDifficulty(); - - ui->label_CurrentBlock_value->setText(QString::number(CurrentBlock)); + if (walletModel && walletModel->getOptionsModel()) { + nDisplayUnit = walletModel->getOptionsModel()->getDisplayUnit(); + if (currentBalance != -1) + setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance, currentZerocoinBalance, currentUnconfirmedZerocoinBalance, currentimmatureZerocoinBalance, + currentWatchOnlyBalance, currentWatchUnconfBalance, currentWatchImmatureBalance); - ui->label_Nethash->setText(tr("Difficulty:")); - ui->label_Nethash_value->setText(QString::number(CurrentDiff, 'f', 4)); + // Update txdelegate->unit with the current unit + txdelegate->unit = nDisplayUnit; - ui->label_CurrentBlockReward_value->setText(QString::number(BlockRewardesbcoin, 'f', 1)); - - ui->label_Supply_value->setText(QString::number(chainActive.Tip()->nMoneySupply / COIN).append(" CCBC")); - } + ui->listTransactions->update(); + } } - void OverviewPage::updateAlerts(const QString& warnings) { - this->ui->labelAlerts->setVisible(!warnings.isEmpty()); - this->ui->labelAlerts->setText(warnings); + this->ui->labelAlerts->setVisible(!warnings.isEmpty()); + this->ui->labelAlerts->setText(warnings); } void OverviewPage::showOutOfSyncWarning(bool fShow) { - ui->labelWalletStatus->setVisible(fShow); - ui->labelTransactionsStatus->setVisible(fShow); -} - -//Button will be released in next update when they are fine tuned. - -/* -void OverviewPage::pushButton_Website() -{ -QDesktopServices::openUrl(QUrl("https://ccbcoin.club/", QUrl::TolerantMode)); -} - -void OverviewPage::pushButton_Explorer() -{ -QDesktopServices::openUrl(QUrl("https://explorer.ccbcoin.club/#/", QUrl::TolerantMode)); -} - -void OverviewPage::pushButton_Github() -{ -QDesktopServices::openUrl(QUrl("https://github.com/CryptoCashBack-Hub/CCBC", QUrl::TolerantMode)); -} - -void OverviewPage::pushButton_Discord() -{ -QDesktopServices::openUrl(QUrl("https://discord.gg/MVVGzv6", QUrl::TolerantMode)); -} - -void OverviewPage::pushButton_Twitter() -{ -QDesktopServices::openUrl(QUrl("https://twitter.com/CCBcoin", QUrl::TolerantMode)); -} -*/ \ No newline at end of file + ui->labelWalletStatus->setVisible(fShow); + ui->labelTransactionsStatus->setVisible(fShow); +} \ No newline at end of file diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index bbdb737..38dde06 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -6,7 +6,6 @@ #define BITCOIN_QT_OVERVIEWPAGE_H #include "amount.h" -#include "main.h" #include @@ -38,31 +37,27 @@ class OverviewPage : public QWidget void showOutOfSyncWarning(bool fShow); public slots: - void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, - //const CAmount& zerocoinBalance, const CAmount& unconfirmedZerocoinBalance, const CAmount& immatureZerocoinBalance, - const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance); + void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& zerocoinBalance, const CAmount& unconfirmedZerocoinBalance, const CAmount& immatureZerocoinBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance); signals: void transactionClicked(const QModelIndex& index); private: QTimer* timer; - QTimer* timerinfo_mn; - QTimer* timerinfo_blockchain; Ui::OverviewPage* ui; ClientModel* clientModel; WalletModel* walletModel; CAmount currentBalance; CAmount currentUnconfirmedBalance; CAmount currentImmatureBalance; - //CAmount currentZerocoinBalance; - //CAmount currentUnconfirmedZerocoinBalance; - //CAmount currentimmatureZerocoinBalance; + CAmount currentZerocoinBalance; + CAmount currentUnconfirmedZerocoinBalance; + CAmount currentimmatureZerocoinBalance; CAmount currentWatchOnlyBalance; CAmount currentWatchUnconfBalance; CAmount currentWatchImmatureBalance; int nDisplayUnit; - //void getPercentage(CAmount nTotalBalance, CAmount nZerocoinBalance, QString& sCCBCPercentage, QString& szCCBCPercentage); + void getPercentage(CAmount nTotalBalance, CAmount nZerocoinBalance, QString& sCCBCPercentage, QString& szCCBCPercentage); TxViewDelegate* txdelegate; TransactionFilterProxy* filter; @@ -72,13 +67,6 @@ private slots: void handleTransactionClicked(const QModelIndex& index); void updateAlerts(const QString& warnings); void updateWatchOnlyLabels(bool showWatchOnly); - //void pushButton_Website(); - //void pushButton_Discord(); - //void pushButton_Github(); - //void pushButton_Twitter(); - //void pushButton_Explorer(); - void updateMasternodeInfo(); - void updatBlockChainInfo(); }; -#endif // BITCOIN_QT_OVERVIEWPAGE_H +#endif // BITCOIN_QT_OVERVIEWPAGE_H \ No newline at end of file diff --git a/src/qt/privacydialog.cpp b/src/qt/privacydialog.cpp index 1225151..54e1927 100644 --- a/src/qt/privacydialog.cpp +++ b/src/qt/privacydialog.cpp @@ -664,7 +664,11 @@ void PrivacyDialog::setBalance(const CAmount& balance, const CAmount& unconfirme nLockedBalance = walletModel->getLockedBalance(); } - ui->labelzBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, zerocoinBalance, false, BitcoinUnits::separatorAlways)); + //ui->labelzBalance->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, zerocoinBalance, false, BitcoinUnits::separatorAlways)); + + //ui->labelzAvailableAmount->setText(QString::number(zerocoinBalance / COIN) + QString(" zCCBC ")); + //ui->labelzAvailableAmount_2->setText(QString::number(matureZerocoinBalance / COIN) + QString(" zCCBC ")); + //ui->labelzCCBCAmountValue->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, balance - immatureBalance - nLockedBalance, false, BitcoinUnits::separatorAlways)); if (pwalletMain) { nLockedBalance = pwalletMain->GetLockedCoins(); diff --git a/src/qt/res/css/default.css b/src/qt/res/css/default.css index 52d9842..50f4caf 100644 --- a/src/qt/res/css/default.css +++ b/src/qt/res/css/default.css @@ -707,8 +707,76 @@ QWidget#AddressBookPage { min-width: 260px; } +/**************************** Privacy Dialog ********************************************/ +QWidget#PrivacyDialog QFrame { + background-color: transparent; + border: 1px solid #000; +} + + QWidget#PrivacyDialog QFrame#labelMintStatus { + /* background-color:transparent; */ + background-color: #eee; + /* border-style: inset; */ + /* border-width:2; */ + /* border:2px solid #00; */ + border: 1px inset gray; + } + +QWidget#PrivacyDialog QLabel { + border: 0px solid #000; +} + + QWidget#PrivacyDialog QLabel#zCCBCLabel { + font-size: 14px; + color: #ffffff; + background-color: #fab43b; + } + + QWidget#PrivacyDialog QLabel#oCCBCLabel { + font-size: 14px; + color: #ffffff; + background-color: #fab43b; + } + +QWidget#PrivacyDialog QPushButton { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #cf8107, stop: .1 #fab43b, stop: .95 #fab43b, stop: 1 #e28901); + border: 0; + border-radius: 3px; + color: #ffffff; + font-size: 12px; + font-weight: normal; + padding-left: 5px; + padding-right: 5px; + padding-top: 5px; + padding-bottom: 5px; +} + + QWidget#PrivacyDialog QPushButton:hover { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f3a90f, stop: .1 #f39910, stop: .95 #f39910, stop: 1 #f3a90f); + } + + QWidget#PrivacyDialog QPushButton:focus { + border: none; + outline: none; + } + + QWidget#PrivacyDialog QPushButton:pressed { + border: 1px solid #333; + } + +QWidget#PrivacyDialog QProgressBar#obfuscationProgress { /* Obfuscation Completion */ + border: 1px solid #818181; + border-radius: 1px; + margin-right: 43px; + text-align: right; + color: #818181; +} + QWidget#PrivacyDialog QProgressBar#obfuscationProgress::chunk { + background-color: #fab43b; + width: 1px; + } /**************************** SETTINGS MENU *********************************************/ @@ -830,79 +898,142 @@ QDialog#EditAddressDialog QLabel { padding-right: 5px; } -/******************************************************************************************/ -/*****************************************************************************************/ -/***************************** Privacy Dialog *******************************************/ -/***************************************************************************************/ -/**************************************************************************************/ +/**************************** OVERVIEW SCREEN *******************************************/ -QWidget#PrivacyDialog QFrame { - background-color: transparent; - border: 1px solid #000; +QWidget .QFrame#frame { /* Wallet Balance */ + min-width: 490px; } - QWidget#PrivacyDialog QFrame#labelMintStatus { - /* background-color:transparent; */ - background-color: #eee; - /* border-style: inset; */ - /* border-width:2; */ - /* border:2px solid #00; */ - border: 1px inset gray; + QWidget .QFrame#frame > .QLabel { + min-width: 190px; + font-weight: normal; + min-height: 30px; } -QWidget#PrivacyDialog QLabel { - border: 0px solid #000; -} + QWidget .QFrame#frame .QLabel#label_5 { /* CCBC Balances Label */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + min-width: 160px; + background-color: transparent; + color: #333; + font-weight: bold; + font-size: 14px; + margin-right: 5px; + padding-right: 5px; + } - QWidget#PrivacyDialog QLabel#zCCBCLabel { + QWidget .QFrame#frame .QLabel#labelWalletStatus { /* Wallet Sync Status */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + margin-left: 3px; + } + + QWidget .QFrame#frame .QLabel#labelSpendable { /* Spendable Header */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 18px; + } + + QWidget .QFrame#frame .QLabel#labelWatchonly { /* Watch-only Header */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 16px; + } + + QWidget .QFrame#frame .QLabel#labelBalanceText { /* Available Balance Label */ + qproperty-alignment: 'AlignVCenter | AlignRight'; + min-width: 160px; + background-color: #333; + color: #fff; + margin-right: 5px; + padding-right: 5px; + font-weight: bold; font-size: 14px; - color: #ffffff; - background-color: #fab43b; + min-height: 35px; } - QWidget#PrivacyDialog QLabel#oCCBCLabel { + QWidget .QFrame#frame .QLabel#labelBalance { /* Available Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + font-weight: bold; + color: #333; + margin-left: 0px; + } + + QWidget .QFrame#frame .QLabel#labelWatchAvailable { /* Watch-only Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 16px; + } + + QWidget .QFrame#frame .QLabel#labelPendingText { /* Pending Balance Label */ + qproperty-alignment: 'AlignVCenter | AlignRight'; + min-width: 160px; + background-color: #333; + color: #fff; + margin-right: 5px; + padding-right: 5px; + font-weight: bold; font-size: 14px; - color: #ffffff; - background-color: #fab43b; + min-height: 35px; } -QWidget#PrivacyDialog QPushButton { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #cf8107, stop: .1 #fab43b, stop: .95 #fab43b, stop: 1 #e28901); - border: 0; - border-radius: 3px; - color: #ffffff; - font-size: 12px; - font-weight: normal; - padding-left: 5px; - padding-right: 5px; - padding-top: 5px; - padding-bottom: 5px; -} + QWidget .QFrame#frame .QLabel#labelUnconfirmed { /* Pending Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 0px; + } - QWidget#PrivacyDialog QPushButton:hover { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f3a90f, stop: .1 #f39910, stop: .95 #f39910, stop: 1 #f3a90f); + QWidget .QFrame#frame .QLabel#labelWatchPending { /* Watch-only Pending Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 16px; } - QWidget#PrivacyDialog QPushButton:focus { - border: none; - outline: none; + QWidget .QFrame#frame .QLabel#labelImmatureText { /* Immature Balance Label */ + qproperty-alignment: 'AlignVCenter | AlignRight'; + min-width: 160px; + background-color: #333; + color: #fff; + margin-right: 5px; + padding-right: 5px; + font-weight: bold; + font-size: 14px; + min-height: 35px; } - QWidget#PrivacyDialog QPushButton:pressed { - border: 1px solid #333; + QWidget .QFrame#frame .QLabel#labelImmature { /* Immature Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 0px; } -QWidget#PrivacyDialog QProgressBar#obfuscationProgress { /* Obfuscation Completion */ - border: 1px solid #818181; - border-radius: 1px; - margin-right: 43px; - text-align: right; - color: #818181; -} + QWidget .QFrame#frame .QLabel#labelWatchImmature { /* Watch-only Immature Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 16px; + } - QWidget#PrivacyDialog QProgressBar#obfuscationProgress::chunk { - background-color: #fab43b; - width: 1px; + QWidget .QFrame#frame .QLabel#labelTotalText { /* Total Balance Label */ + qproperty-alignment: 'AlignVCenter | AlignRight'; + min-width: 160px; + background-color: #333; + color: #fff; + margin-right: 5px; + padding-right: 5px; + font-weight: bold; + font-size: 14px; + min-height: 35px; + } + + QWidget .QFrame#frame .QLabel#labelTotal { /* Total Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 0px; + } + + QWidget .QFrame#frame .QLabel#labelWatchTotal { /* Watch-only Total Balance */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + font-size: 12px; + margin-left: 16px; } QWidget .QFrame#frame_4 .QLabel#label_5z_3 { /* Zerocoin Balance Label */ @@ -996,7 +1127,18 @@ QWidget .QFrame#frame_4 .QLabel#labelzBalanceImmature { /* Immature zCCBC Balanc margin-left: 0px; } -QWidget .QFrame#frame_3 .QLabel#labelzBalanceTextz { /* Available zCCBC Label */ +QWidget .QFrame#frame_3 .QLabel#label_5z { /* Combined Balance Label */ + qproperty-alignment: 'AlignVCenter | AlignLeft'; + min-width: 160px; + background-color: transparent; + color: #333; + font-weight: bold; + font-size: 14px; + margin-right: 5px; + padding-right: 5px; +} + +QWidget .QFrame#frame_3 .QLabel#labelBalanceTextz { /* Available CCBC Label */ qproperty-alignment: 'AlignVCenter | AlignRight'; min-width: 160px; background-color: #333; @@ -1008,7 +1150,7 @@ QWidget .QFrame#frame_3 .QLabel#labelzBalanceTextz { /* Available zCCBC Label */ /* min-height:35px; */ } -QWidget .QFrame#frame_3 .QLabel#labelzBalancez { /* Available zCCBC Balance */ +QWidget .QFrame#frame_3 .QLabel#labelBalancez { /* Available CCBC Balance */ qproperty-alignment: 'AlignVCenter | AlignLeft'; font-size: 12px; font-weight: bold; @@ -1016,7 +1158,7 @@ QWidget .QFrame#frame_3 .QLabel#labelzBalancez { /* Available zCCBC Balance */ margin-left: 0px; } -QWidget .QFrame#frame_3 .QLabel#labelLockedBalanceText { /* Available zCCBC Label */ +QWidget .QFrame#frame_3 .QLabel#labelzBalanceTextz { /* Available zCCBC Label */ qproperty-alignment: 'AlignVCenter | AlignRight'; min-width: 160px; background-color: #333; @@ -1028,15 +1170,15 @@ QWidget .QFrame#frame_3 .QLabel#labelLockedBalanceText { /* Available zCCBC Labe /* min-height:35px; */ } -QWidget .QFrame#frame_3 .QLabel#labelLockedBalance { /* Available zCCBC Balance */ +QWidget .QFrame#frame_3 .QLabel#labelzBalancez { /* Available zCCBC Balance */ qproperty-alignment: 'AlignVCenter | AlignLeft'; font-size: 12px; - font-weight: normal; + font-weight: bold; color: #333; margin-left: 0px; } -QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalanceText { /* Available zCCBC Label */ +QWidget .QFrame#frame_3 .QLabel#labelLockedBalanceText { /* Available zCCBC Label */ qproperty-alignment: 'AlignVCenter | AlignRight'; min-width: 160px; background-color: #333; @@ -1048,7 +1190,7 @@ QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalanceText { /* Available zCCBC La /* min-height:35px; */ } -QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalance { /* Available zCCBC Balance */ +QWidget .QFrame#frame_3 .QLabel#labelLockedBalance { /* Available zCCBC Balance */ qproperty-alignment: 'AlignVCenter | AlignLeft'; font-size: 12px; font-weight: normal; @@ -1056,450 +1198,26 @@ QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalance { /* Available zCCBC Balanc margin-left: 0px; } -/******************************************************************************************/ -/*****************************************************************************************/ -/**************************** OVERVIEW SCREEN *******************************************/ -/***************************************************************************************/ -/**************************************************************************************/ - -QWidget .QFrame#frame { /* Wallet Balance */ - min-width: 490px; -} - - QWidget .QFrame#frame > .QLabel { - min-width: 190px; - font-weight: normal; - min-height: 30px; - } - - - -/*************** Frame_bottom_1 section*****************/ -/********************* Masternodes *********************/ - -QWidget .QFrame#frameInfo { /*bottom frame 1 - info */ - background-color: #ffffff; - border-style: none; - border: 1px solid; - border-color: #d2d2d2; -} - -QWidget .QFrame#frame_bottom_1 { - border-style: solid; -} - -QWidget .QFrame#frameMnInfo { - border-style: 0px; -} - -QWidget .QFrame#frameMnInfoLevels { - border-style: 0px; - border-top: 1px solid #d2d2d2; - border-top-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #ffffff, stop:0.2 #d2d2d2, stop:0.8 #d2d2d2, stop:1 #ffffff); -} - -QWidget .QFrame#frameBlockchainInfo { - border-style: 0px; -} - -QWidget .QFrame#frameBlockchainInfoData { - border-style: 0px; - border-top: 1px solid #d2d2d2; - border-top-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #ffffff, stop:0.2 #d2d2d2, stop:0.8 #d2d2d2, stop:1 #ffffff); -} - - -QWidget .QProgressBar#graphMN { - color: #000000; - border: 0px solid; - border-color: #d2d2d2; - /* border-left:1px solid #d2d2d2; - border-radius:0px;*/ - background-color: transparent; - font-size: 12px; - text-align: center; -} - -/*******************************/ -/*Start of the Balance section*/ -/******************************/ -QWidget .QFrame#frame .QLabel#label_5 { /* CCBC Balances Label */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; +QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalanceText { /* Available zCCBC Label */ + qproperty-alignment: 'AlignVCenter | AlignRight'; min-width: 160px; - background-color: transparent; - color: #333; - font-weight: bold; - font-size: 14px; + background-color: #333; + color: #fff; margin-right: 5px; padding-right: 5px; + font-weight: bold; + font-size: 14px; + /* min-height:35px; */ } -QWidget .QFrame#frame .QLabel#labelWalletStatus { /* Wallet Sync Status */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - margin-left: 3px; -} - -QWidget .QFrame#frame .QLabel#labelSpendable { /* Spendable Header */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 18px; -} - -QWidget .QFrame#frame .QLabel#labelWatchonly { /* Watch-only Header */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 16px; -} - -QWidget .QFrame#frame .QLabel#labelBalanceText { /* Available Balance Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#labelBalance { /* Available Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#labelWatchAvailable { /* Watch-only Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 16px; -} - -QWidget .QFrame#frame .QLabel#labelPendingText { /* Pending Balance Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#labelUnconfirmed { /* Pending Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#labelWatchPending { /* Watch-only Pending Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 16px; -} - -QWidget .QFrame#frame .QLabel#labelImmatureText { /* Immature Balance Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#labelImmature { /* Immature Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#labelWatchImmature { /* Watch-only Immature Balance */ +QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalance { /* Available zCCBC Balance */ qproperty-alignment: 'AlignVCenter | AlignLeft'; font-size: 12px; - margin-left: 16px; -} - -QWidget .QFrame#frame .QLabel#labelTotalText { /* Total Balance Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#labelTotal { /* Total Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#labelWatchTotal { /* Watch-only Total Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - margin-left: 16px; -} - -/***************************/ -/*End of the Balances Area*/ -/*************************/ - -/**************************************************************/ -/********************** ROI Section***************************/ -/**********************Overview Page*************************/ -/************************************************************/ -QWidget .QFrame#frame .QLabel#label_6 { /*MN Header Section Label*/ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - min-width: 160px; - background-color: transparent; - color: #333; - font-weight: bold; - font-size: 14px; - margin-right: 5px; - padding-right: 5px; -} - -QWidget .QFrame#frame .QLabel#label_Needed_Collat { /* Collateral Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#label_lcolat { /* Collateral Amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#label_MN_Online { /* MN Online Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#labelMnTotal_Value { /* MN Total amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#label_Daily_Rewards { /* Daily Rewards Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#roi { /* ROI amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#label_Days_ROI { /* Days Till ROI Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#roi_1 { /* ROI amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -/******************************************************************/ -/**************************** Network Section*********************/ -/****************************************************************/ -QWidget .QFrame#frame .QLabel#label_7 { /*Network Header Section Label*/ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - min-width: 160px; - background-color: transparent; - color: #333; - font-weight: bold; - font-size: 14px; - margin-right: 5px; - padding-right: 5px; -} - -QWidget .QFrame#frame .QLabel#label_CurrentBlock { /* Current Block Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#label_CurrentBlock_value { /* Current Block amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#label_CurrentBlockReward { /* Current Block Reward Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#label_CurrentBlockReward_value { /* Current Block reward amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#label_Nethash { /* Network Hash Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#label_Nethash_value { /* Network Hash amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} - -QWidget .QFrame#frame .QLabel#label_Supply { /* Coin Supply Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - min-height: 35px; -} - -QWidget .QFrame#frame .QLabel#label_Supply_value { /* Coin Supply amount */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; - color: #333; - margin-left: 0px; -} -/********************************************************************************/ -/****************************URL LINKS******************************************/ -/******************************************************************************/ - -QWidget .QFrame#frame .QPushButton#pushButton_Website { /*Website Button*/ - background-image: url(':/icons/website.png'); -} - -QWidget .QFrame#frame .QPushButton#pushButton_Discord { /*Discord Button*/ - background-image: url(':/icons/discord.png'); -} - -QWidget .QFrame#frame .QPushButton#pushButton_Github { /*Github Button*/ - background-image: url(':/icons/github.png'); -} - -QWidget .QFrame#frame .QPushButton#pushButton_Twitter { /*Twitter Button*/ - background-image: url(':/icons/twitter.png'); -} - -QWidget .QFrame#frame .QPushButton#pushButton_Explorer { /*Explorer Button*/ - background-image: url(':/icons/explorer.png'); -} - -/*These are the left over combined labels and qt items*/ - -QWidget .QFrame#frame_3 .QLabel#label_5z { /* Combined Balance Label */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - min-width: 160px; - background-color: transparent; - color: #333; - font-weight: bold; - font-size: 14px; - margin-right: 5px; - padding-right: 5px; -} - -QWidget .QFrame#frame_3 .QLabel#labelBalanceTextz { /* Available CCBC Label */ - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 160px; - background-color: #333; - color: #fff; - margin-right: 5px; - padding-right: 5px; - font-weight: bold; - font-size: 14px; - /* min-height:35px; */ -} - -QWidget .QFrame#frame_3 .QLabel#labelBalancez { /* Available CCBC Balance */ - qproperty-alignment: 'AlignVCenter | AlignLeft'; - font-size: 12px; - font-weight: bold; + font-weight: normal; color: #333; margin-left: 0px; } - - QWidget .QFrame#frame_3 .QLabel#labelTotalTextz { /* Available total Label */ qproperty-alignment: 'AlignVCenter | AlignRight'; min-width: 160px; @@ -1520,11 +1238,7 @@ QWidget .QFrame#frame_3 .QLabel#labelTotalz { /* Available total Balance */ margin-left: 0px; } -/******************************************************************************************/ -/*****************************************************************************************/ /**************************** RECENT TRANSACTIONS ***************************************/ -/***************************************************************************************/ -/**************************************************************************************/ QWidget .QFrame#frame_2 { /* Transactions Widget */ min-width: 410px; diff --git a/src/spork.cpp b/src/spork.cpp index 9c9ee12..3470ac5 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -87,6 +87,14 @@ void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) LogPrintf("spork - new %s ID %d Time %d bestHeight %d\n", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Tip()->nHeight); + if (spork.nTimeSigned >= Params().NewSporkStart()) { + if (!sporkManager.CheckSignature(spork, true)) { + LogPrintf("%s : Invalid Signature\n", __func__); + Misbehaving(pfrom->GetId(), 100); + return; + } + } + if (!sporkManager.CheckSignature(spork)) { LogPrintf("spork - invalid signature\n"); Misbehaving(pfrom->GetId(), 100); @@ -182,17 +190,29 @@ void ReprocessBlocks(int nBlocks) } } -bool CSporkManager::CheckSignature(CSporkMessage& spork) +bool CSporkManager::CheckSignature(CSporkMessage& spork, bool fCheckSigner) { //note: need to investigate why this is failing std::string strMessage = boost::lexical_cast(spork.nSporkID) + boost::lexical_cast(spork.nValue) + boost::lexical_cast(spork.nTimeSigned); CPubKey pubkeynew(ParseHex(Params().SporkKey())); std::string errorMessage = ""; - if (obfuScationSigner.VerifyMessage(pubkeynew, spork.vchSig, strMessage, errorMessage)) { - return true; - } + //if (obfuScationSigner.VerifyMessage(pubkeynew, spork.vchSig, strMessage, errorMessage)) { + //return true; + //} + + bool fValidWithNewKey = obfuScationSigner.VerifyMessage(pubkeynew, spork.vchSig, strMessage, errorMessage); + + if (fCheckSigner && !fValidWithNewKey) + return false; + + // See if window is open that allows for old spork key to sign messages + if (!fValidWithNewKey && GetAdjustedTime() < Params().RejectOldSporkKey()) { + CPubKey pubkeyold(ParseHex(Params().SporkKeyOld())); + return obfuScationSigner.VerifyMessage(pubkeyold, spork.vchSig, strMessage, errorMessage); + } + + return fValidWithNewKey; - return false; } bool CSporkManager::Sign(CSporkMessage& spork) diff --git a/src/spork.h b/src/spork.h index 90b6229..2bb40a4 100644 --- a/src/spork.h +++ b/src/spork.h @@ -122,7 +122,7 @@ class CSporkManager int GetSporkIDByName(std::string strName); bool UpdateSpork(int nSporkID, int64_t nValue); bool SetPrivKey(std::string strPrivKey); - bool CheckSignature(CSporkMessage& spork); + bool CheckSignature(CSporkMessage& spork, bool fCheckSigner = false); bool Sign(CSporkMessage& spork); void Relay(CSporkMessage& msg); }; diff --git a/src/version.h b/src/version.h index c7886c9..2a98838 100644 --- a/src/version.h +++ b/src/version.h @@ -12,7 +12,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 70006; +static const int PROTOCOL_VERSION = 70007; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -22,7 +22,7 @@ static const int GETHEADERS_VERSION = 70000; //! disconnect from peers older than this proto version static const int MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT = 70005; -static const int MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT = 70006; +static const int MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT = 70007; //! nTime field added to CAddress, starting with this version; //! if possible, avoid requesting addresses nodes older than this diff --git a/src/wallet.cpp b/src/wallet.cpp index 096723a..039c391 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -28,8 +28,8 @@ #include #include -#include #include +#include using namespace std; @@ -1304,11 +1304,11 @@ CAmount CWallet::GetZerocoinBalance(bool fMatureOnly) const } } for (auto& denom : libzerocoin::zerocoinDenomList) { - LogPrint("zero","%s My coins for denomination %d pubcoin %s\n", __func__,denom, myZerocoinSupply.at(denom)); + LogPrint("zero", "%s My coins for denomination %d pubcoin %s\n", __func__, denom, myZerocoinSupply.at(denom)); } - LogPrint("zero","Total value of coins %d\n",nTotal); + LogPrint("zero", "Total value of coins %d\n", nTotal); - if (nTotal < 0 ) nTotal = 0; // Sanity never hurts + if (nTotal < 0) nTotal = 0; // Sanity never hurts return nTotal; } @@ -1323,15 +1323,15 @@ CAmount CWallet::GetUnconfirmedZerocoinBalance() const CAmount nUnconfirmed = 0; CWalletDB walletdb(pwalletMain->strWalletFile); list listMints = walletdb.ListMintedCoins(true, false, true); - + std::map mapUnconfirmed; - for (const auto& denom : libzerocoin::zerocoinDenomList){ + for (const auto& denom : libzerocoin::zerocoinDenomList) { mapUnconfirmed.insert(make_pair(denom, 0)); } { LOCK2(cs_main, cs_wallet); - for (auto& mint : listMints){ + for (auto& mint : listMints) { if (!mint.GetHeight() || mint.GetHeight() > chainActive.Height() - Params().Zerocoin_MintRequiredConfirmations()) { libzerocoin::CoinDenomination denom = mint.GetDenomination(); nUnconfirmed += libzerocoin::ZerocoinDenominationToAmount(denom); @@ -1341,12 +1341,12 @@ CAmount CWallet::GetUnconfirmedZerocoinBalance() const } for (auto& denom : libzerocoin::zerocoinDenomList) { - LogPrint("zero","%s My unconfirmed coins for denomination %d pubcoin %s\n", __func__,denom, mapUnconfirmed.at(denom)); + LogPrint("zero", "%s My unconfirmed coins for denomination %d pubcoin %s\n", __func__, denom, mapUnconfirmed.at(denom)); } - LogPrint("zero","Total value of unconfirmed coins %ld\n", nUnconfirmed); + LogPrint("zero", "Total value of unconfirmed coins %ld\n", nUnconfirmed); - if (nUnconfirmed < 0 ) nUnconfirmed = 0; // Sanity never hurts + if (nUnconfirmed < 0) nUnconfirmed = 0; // Sanity never hurts return nUnconfirmed; } @@ -1626,15 +1626,15 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const found = IsDenominatedAmount(pcoin->vout[i].nValue); } else if (nCoinType == ONLY_NOT10000IFMN) { found = !(fMasterNode && pcoin->vout[i].nValue == 25000 * COIN); - //found = !(fMasterNode && pcoin->vout[i].nValue == Params().MasternodeCollateralAmt()*COIN); + //found = !(fMasterNode && pcoin->vout[i].nValue == Params().MasternodeCollateralAmt()*COIN); } else if (nCoinType == ONLY_NONDENOMINATED_NOT10000IFMN) { if (IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts found = !IsDenominatedAmount(pcoin->vout[i].nValue); if (found && fMasterNode) found = pcoin->vout[i].nValue != 25000 * COIN; // do not use Hot MN funds - //if (found && fMasterNode) found = pcoin->vout[i].nValue != Params().MasternodeCollateralAmt()*COIN; // do not use Hot MN funds + //if (found && fMasterNode) found = pcoin->vout[i].nValue != Params().MasternodeCollateralAmt()*COIN; // do not use Hot MN funds } else if (nCoinType == ONLY_10000) { found = pcoin->vout[i].nValue == 25000 * COIN; - //found = pcoin->vout[i].nValue == Params().MasternodeCollateralAmt()*COIN; + //found = pcoin->vout[i].nValue == Params().MasternodeCollateralAmt()*COIN; } else { found = true; } @@ -1948,7 +1948,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, setvout[out.i].nValue == v //make sure it's the denom we're looking for && nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1 * COIN) + 100 //round the amount up to .1 CCBC over - ) { + ) { CTxIn vin = CTxIn(out.tx->GetHash(), out.i); int rounds = GetInputObfuscationRounds(vin); // make sure it's actually anonymized @@ -2810,7 +2810,6 @@ string CWallet::PrepareObfuscationDenominate(int minRounds, int maxRounds) /* Select the coins we'll use - if minRounds >= 0 it means only denominated inputs are going in and coming out */ if (minRounds >= 0) { @@ -3533,7 +3532,7 @@ void CWallet::AutoZeromint() if (GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) return; // Wait until blockchain + masternodes are fully synced and wallet is unlocked. - if (!masternodeSync.IsSynced() || IsLocked()){ + if (!masternodeSync.IsSynced() || IsLocked()) { // Re-adjust startup time in case syncing needs a long time. nStartupTime = GetAdjustedTime(); return; @@ -3541,19 +3540,19 @@ void CWallet::AutoZeromint() // After sync wait even more to reduce load when wallet was just started int64_t nWaitTime = GetAdjustedTime() - nStartupTime; - if (nWaitTime < AUTOMINT_DELAY){ + if (nWaitTime < AUTOMINT_DELAY) { LogPrint("zero", "CWallet::AutoZeromint(): time since sync-completion or last Automint (%ld sec) < default waiting time (%ld sec). Waiting again...\n", nWaitTime, AUTOMINT_DELAY); return; } CAmount nZerocoinBalance = GetZerocoinBalance(false); //false includes both pending and mature zerocoins. Need total balance for this so nothing is overminted. - CAmount nBalance = GetUnlockedCoins(); // We only consider unlocked coins, this also excludes masternode-vins - // from being accidentally minted + CAmount nBalance = GetUnlockedCoins(); // We only consider unlocked coins, this also excludes masternode-vins + // from being accidentally minted CAmount nMintAmount = 0; CAmount nToMintAmount = 0; // zCCBC are integers > 0, so we can't mint 10% of 9 CCBC - if (nBalance < 10){ + if (nBalance < 10) { LogPrint("zero", "CWallet::AutoZeromint(): available balance (%ld) too small for minting zCCBC\n", nBalance); return; } @@ -3562,9 +3561,9 @@ void CWallet::AutoZeromint() double dPercentage = 100 * (double)nZerocoinBalance / (double)(nZerocoinBalance + nBalance); // Check if minting is actually needed - if(dPercentage >= nZeromintPercentage){ + if (dPercentage >= nZeromintPercentage) { LogPrint("zero", "CWallet::AutoZeromint() @block %ld: percentage of existing zCCBC (%lf%%) already >= configured percentage (%d%%). No minting needed...\n", - chainActive.Tip()->nHeight, dPercentage, nZeromintPercentage); + chainActive.Tip()->nHeight, dPercentage, nZeromintPercentage); return; } @@ -3579,42 +3578,42 @@ void CWallet::AutoZeromint() // possible denominations to avoid having 5000 denominations only. // If a preferred denomination is used (means nPreferredDenom != 0) do nothing until we have enough CCBC to mint this denomination - if (nPreferredDenom > 0){ + if (nPreferredDenom > 0) { if (nToMintAmount >= nPreferredDenom) - nToMintAmount = nPreferredDenom; // Enough coins => mint preferred denomination + nToMintAmount = nPreferredDenom; // Enough coins => mint preferred denomination else - nToMintAmount = 0; // Not enough coins => do nothing and wait for more coins + nToMintAmount = 0; // Not enough coins => do nothing and wait for more coins } - if (nToMintAmount >= ZQ_6666){ + if (nToMintAmount >= ZQ_6666) { nMintAmount = ZQ_6666; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIVE_THOUSAND){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIVE_THOUSAND) { nMintAmount = libzerocoin::CoinDenomination::ZQ_FIVE_THOUSAND; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_ONE_THOUSAND){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_ONE_THOUSAND) { nMintAmount = libzerocoin::CoinDenomination::ZQ_ONE_THOUSAND; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIVE_HUNDRED){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIVE_HUNDRED) { nMintAmount = libzerocoin::CoinDenomination::ZQ_FIVE_HUNDRED; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_ONE_HUNDRED){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_ONE_HUNDRED) { nMintAmount = libzerocoin::CoinDenomination::ZQ_ONE_HUNDRED; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIFTY){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIFTY) { nMintAmount = libzerocoin::CoinDenomination::ZQ_FIFTY; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_TEN){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_TEN) { nMintAmount = libzerocoin::CoinDenomination::ZQ_TEN; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIVE){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_FIVE) { nMintAmount = libzerocoin::CoinDenomination::ZQ_FIVE; - } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_ONE){ + } else if (nToMintAmount >= libzerocoin::CoinDenomination::ZQ_ONE) { nMintAmount = libzerocoin::CoinDenomination::ZQ_ONE; } else { nMintAmount = 0; } - if (nMintAmount > 0){ - CWalletTx wtx; + if (nMintAmount > 0) { + CWalletTx wtx; vector vMints; - string strError = pwalletMain->MintZerocoin(nMintAmount*COIN, wtx, vMints); + string strError = pwalletMain->MintZerocoin(nMintAmount * COIN, wtx, vMints); // Return if something went wrong during minting - if (strError != ""){ + if (strError != "") { LogPrintf("CWallet::AutoZeromint(): auto minting failed with error: %s\n", strError); return; } @@ -3622,11 +3621,10 @@ void CWallet::AutoZeromint() nBalance = GetUnlockedCoins(); dPercentage = 100 * (double)nZerocoinBalance / (double)(nZerocoinBalance + nBalance); LogPrintf("CWallet::AutoZeromint() @ block %ld: successfully minted %ld zCCBC. Current percentage of zCCBC: %lf%%\n", - chainActive.Tip()->nHeight, nMintAmount, dPercentage); + chainActive.Tip()->nHeight, nMintAmount, dPercentage); // Re-adjust startup time to delay next Automint for 5 minutes nStartupTime = GetAdjustedTime(); - } - else { + } else { LogPrintf("CWallet::AutoZeromint(): Nothing minted because either not enough funds available or the requested denomination size (%d) is not yet reached.\n", nPreferredDenom); } } @@ -3986,7 +3984,7 @@ bool CWallet::CreateZerocoinMintTransaction(const CAmount nValue, CMutableTransa libzerocoin::PublicCoin pubCoin = newCoin.getPublicCoin(); // Validate - if(!pubCoin.validate()) { + if (!pubCoin.validate()) { strFailReason = _("failed to validate zerocoin"); return false; } @@ -4120,8 +4118,7 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con CDataStream serializedCoinSpendChecking(SER_NETWORK, PROTOCOL_VERSION); try { serializedCoinSpendChecking << spend; - } - catch (...) { + } catch (...) { receipt.SetStatus("failed to deserialize", ZCCBC_BAD_SERIALIZATION); return false; } @@ -4150,8 +4147,7 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con CZerocoinSpend zcSpend(spend.getCoinSerialNumber(), 0, zerocoinSelected.GetValue(), zerocoinSelected.GetDenomination(), nAccumulatorChecksum); zcSpend.SetMintCount(nMintsAdded); receipt.AddSpend(zcSpend); - } - catch (const std::exception&) { + } catch (const std::exception&) { receipt.SetStatus("CoinSpend: Accumulator witness does not verify", ZCCBC_INVALID_WITNESS); return false; } @@ -4161,7 +4157,7 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con return true; } -bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, CWalletTx& wtxNew, CReserveKey& reserveKey, CZerocoinSpendReceipt& receipt, vector& vSelectedMints, vector& vNewMints, bool fMintChange, bool fMinimizeChange, CBitcoinAddress* address) +bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, CWalletTx& wtxNew, CReserveKey& reserveKey, CZerocoinSpendReceipt& receipt, vector& vSelectedMints, vector& vNewMints, bool fMintChange, bool fMinimizeChange, CBitcoinAddress* address) { // Check available funds int nStatus = ZCCBC_TRX_FUNDS_PROBLEMS; @@ -4182,12 +4178,12 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, CWalletDB walletdb(pwalletMain->strWalletFile); list listMints; CAmount nValueSelected = 0; - int nCoinsReturned = 0; // Number of coins returned in change from function below (for debug) - int nNeededSpends = 0; // Number of spends which would be needed if selection failed + int nCoinsReturned = 0; // Number of coins returned in change from function below (for debug) + int nNeededSpends = 0; // Number of spends which would be needed if selection failed const int nMaxSpends = Params().Zerocoin_MaxSpendsPerTransaction(); // Maximum possible spends for one zCCBC transaction if (vSelectedMints.empty()) { listMints = walletdb.ListMintedCoins(true, true, true); // need to find mints to spend - if(listMints.empty()) { + if (listMints.empty()) { receipt.SetStatus("failed to find Zerocoins in in wallet.dat", nStatus); return false; } @@ -4196,13 +4192,13 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, double dValue = static_cast(nValue) / static_cast(COIN); bool fWholeNumber = floor(dValue) == dValue; CAmount nValueToSelect = nValue; - if(!fWholeNumber) + if (!fWholeNumber) nValueToSelect = static_cast(ceil(dValue) * COIN); // Select the zCcbc mints to use in this spend std::map DenomMap = GetMyZerocoinDistribution(); vSelectedMints = SelectMintsFromList(nValueToSelect, nValueSelected, nMaxSpends, fMinimizeChange, - nCoinsReturned, listMints, DenomMap, nNeededSpends); + nCoinsReturned, listMints, DenomMap, nNeededSpends); } else { for (const CZerocoinMint mint : vSelectedMints) nValueSelected += ZerocoinDenominationToAmount(mint.GetDenomination()); @@ -4244,11 +4240,10 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, return false; if (vSelectedMints.empty()) { - if(nNeededSpends > 0){ + if (nNeededSpends > 0) { // Too much spends needed, so abuse nStatus to report back the number of needed spends receipt.SetStatus("Too much spends needed", nStatus, nNeededSpends); - } - else { + } else { receipt.SetStatus("failed to select a zerocoin", nStatus); } return false; @@ -4354,7 +4349,7 @@ string CWallet::ResetMintZerocoin(bool fExtendedSearch) CWalletDB walletdb(pwalletMain->strWalletFile); list listMints = walletdb.ListMintedCoins(false, false, true); - vector vMintsToFind{ std::make_move_iterator(std::begin(listMints)), std::make_move_iterator(std::end(listMints)) }; + vector vMintsToFind{std::make_move_iterator(std::begin(listMints)), std::make_move_iterator(std::end(listMints))}; vector vMintsMissing; vector vMintsToUpdate; @@ -4614,7 +4609,7 @@ bool CWallet::SpendZerocoin(CAmount nAmount, int nSecurityLevel, CWalletTx& wtxN walletdb.WriteZerocoinMint(mint); } - receipt.SetStatus("Spend Successful", ZCCBC_SPEND_OKAY); // When we reach this point spending zCCBC was successful + receipt.SetStatus("Spend Successful", ZCCBC_SPEND_OKAY); // When we reach this point spending zCCBC was successful return true; -} +} \ No newline at end of file diff --git a/src/wallet.h b/src/wallet.h index 9dc7ea1..29be3a8 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -81,27 +81,27 @@ enum AvailableCoinsType { ONLY_DENOMINATED = 2, ONLY_NOT10000IFMN = 3, ONLY_NONDENOMINATED_NOT10000IFMN = 4, // ONLY_NONDENOMINATED and not 10000 CCBC at the same time - ONLY_10000 = 5, // find masternode outputs including locked ones (use with caution) - STAKABLE_COINS = 6 // UTXO's that are valid for staking + ONLY_10000 = 5, // find masternode outputs including locked ones (use with caution) + STAKABLE_COINS = 6 // UTXO's that are valid for staking }; // Possible states for zCCBC send enum ZerocoinSpendStatus { - ZCCBC_SPEND_OKAY = 0, // No error - ZCCBC_SPEND_ERROR = 1, // Unspecified class of errors, more details are (hopefully) in the returning text - ZCCBC_WALLET_LOCKED = 2, // Wallet was locked - ZCCBC_COMMIT_FAILED = 3, // Commit failed, reset status - ZCCBC_ERASE_SPENDS_FAILED = 4, // Erasing spends during reset failed - ZCCBC_ERASE_NEW_MINTS_FAILED = 5, // Erasing new mints during reset failed - ZCCBC_TRX_FUNDS_PROBLEMS = 6, // Everything related to available funds - ZCCBC_TRX_CREATE = 7, // Everything related to create the transaction - ZCCBC_TRX_CHANGE = 8, // Everything related to transaction change - ZCCBC_TXMINT_GENERAL = 9, // General errors in MintToTxIn - ZCCBC_INVALID_COIN = 10, // Selected mint coin is not valid - ZCCBC_FAILED_ACCUMULATOR_INITIALIZATION = 11, // Failed to initialize witness - ZCCBC_INVALID_WITNESS = 12, // Spend coin transaction did not verify - ZCCBC_BAD_SERIALIZATION = 13, // Transaction verification failed - ZCCBC_SPENT_USED_ZCCBC = 14 // Coin has already been spend + ZCCBC_SPEND_OKAY = 0, // No error + ZCCBC_SPEND_ERROR = 1, // Unspecified class of errors, more details are (hopefully) in the returning text + ZCCBC_WALLET_LOCKED = 2, // Wallet was locked + ZCCBC_COMMIT_FAILED = 3, // Commit failed, reset status + ZCCBC_ERASE_SPENDS_FAILED = 4, // Erasing spends during reset failed + ZCCBC_ERASE_NEW_MINTS_FAILED = 5, // Erasing new mints during reset failed + ZCCBC_TRX_FUNDS_PROBLEMS = 6, // Everything related to available funds + ZCCBC_TRX_CREATE = 7, // Everything related to create the transaction + ZCCBC_TRX_CHANGE = 8, // Everything related to transaction change + ZCCBC_TXMINT_GENERAL = 9, // General errors in MintToTxIn + ZCCBC_INVALID_COIN = 10, // Selected mint coin is not valid + ZCCBC_FAILED_ACCUMULATOR_INITIALIZATION = 11, // Failed to initialize witness + ZCCBC_INVALID_WITNESS = 12, // Spend coin transaction did not verify + ZCCBC_BAD_SERIALIZATION = 13, // Transaction verification failed + ZCCBC_SPENT_USED_ZCCBC = 14 // Coin has already been spend }; struct CompactTallyItem { @@ -199,7 +199,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface // Zerocoin additions bool CreateZerocoinMintTransaction(const CAmount nValue, CMutableTransaction& txNew, vector& vMints, CReserveKey* reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl = NULL, const bool isZCSpendChange = false); - bool CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, CWalletTx& wtxNew, CReserveKey& reserveKey, CZerocoinSpendReceipt& receipt, vector& vSelectedMints, vector& vNewMints, bool fMintChange, bool fMinimizeChange, CBitcoinAddress* address = NULL); + bool CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel, CWalletTx& wtxNew, CReserveKey& reserveKey, CZerocoinSpendReceipt& receipt, vector& vSelectedMints, vector& vNewMints, bool fMintChange, bool fMinimizeChange, CBitcoinAddress* address = NULL); bool MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, const uint256& hashTxOut, CTxIn& newTxIn, CZerocoinSpendReceipt& receipt); std::string MintZerocoin(CAmount nValue, CWalletTx& wtxNew, vector& vMints, const CCoinControl* coinControl = NULL); bool SpendZerocoin(CAmount nValue, int nSecurityLevel, CWalletTx& wtxNew, CZerocoinSpendReceipt& receipt, vector& vMintsSelected, bool fMintChange, bool fMinimizeChange, CBitcoinAddress* addressTo = NULL); @@ -313,7 +313,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface { fBackupMints = fEnabled; } - + bool isMultiSendEnabled() { return fMultiSendMasternodeReward || fMultiSendStake; @@ -1125,7 +1125,7 @@ class CWalletTx : public CMerkleTx return nCredit; } - // Return sum of unlocked coins + // Return sum of unlocked coins CAmount GetLockedCredit() const { if (pwallet == 0) @@ -1478,4 +1478,4 @@ class CAccountingEntry std::vector _ssExtra; }; -#endif // BITCOIN_WALLET_H +#endif // BITCOIN_WALLET_H \ No newline at end of file