diff --git a/src/main.cpp b/src/main.cpp index 48324ca42..3255c3380 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,9 @@ #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" +#include "deprecation.h" #include "init.h" #include "merkleblock.h" #include "metrics.h" @@ -27,6 +29,7 @@ #include "utilmoneystr.h" #include "validationinterface.h" #include "wallet/asyncrpcoperation_sendmany.h" +#include "wallet/asyncrpcoperation_shieldcoinbase.h" #include @@ -50,7 +53,9 @@ using namespace std; CCriticalSection cs_main; extern uint8_t NOTARY_PUBKEY33[33]; -extern int32_t KOMODO_LOADINGBLOCKS; +extern int32_t KOMODO_LOADINGBLOCKS,KOMODO_LONGESTCHAIN; +int32_t KOMODO_NEWBLOCKS; +void komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); BlockMap mapBlockIndex; CChain chainActive; @@ -73,6 +78,8 @@ size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; +unsigned int expiryDelta = DEFAULT_TX_EXPIRY_DELTA; + /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -100,30 +107,30 @@ const string strMessageMagic = "Komodo 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; } }; - + 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, and pruning nodes may be @@ -133,10 +140,10 @@ namespace { /** 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. - * Pruned nodes may have entries where B is missing data. - */ + * Pruned nodes may have entries where B is missing data. + */ multimap mapBlocksUnlinked; - + CCriticalSection cs_LastBlockFile; std::vector vinfoBlockFile; int nLastBlockFile = 0; @@ -145,7 +152,7 @@ namespace { * or if we allocate more file space when we're in prune mode */ bool fCheckForPruning = false; - + /** * Every received block is assigned a unique and increasing identifier, so we * know which one to give priority in case of a fork. @@ -153,14 +160,14 @@ namespace { CCriticalSection cs_nBlockSequenceId; /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ uint32_t nBlockSequenceId = 1; - + /** * Sources of received blocks, saved to be able to send them reject * messages or ban them when processing happens afterwards. Protected by * cs_main. */ map mapBlockSource; - + /** * Filter for transactions that were recently rejected by * AcceptToMemoryPool. These are not rerequested until the chain tip @@ -183,7 +190,7 @@ namespace { */ boost::scoped_ptr recentRejects; uint256 hashRecentRejectsChainTip; - + /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; @@ -193,16 +200,16 @@ namespace { int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; - + /** Number of blocks in flight with validated headers. */ int nQueuedValidatedHeaders = 0; - + /** Number of preferable block download peers. */ int nPreferredDownload = 0; - + /** Dirty block index entries. */ set setDirtyBlockIndex; - + /** Dirty block file entries. */ set setDirtyFileInfo; } // anon namespace @@ -213,306 +220,306 @@ namespace { // 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; - int nBlocksInFlightValidHeaders; - //! Whether we consider this a preferred download peer. - bool fPreferredDownload; - - CNodeState() { - fCurrentlyConnected = false; - nMisbehavior = 0; - fShouldBan = false; - pindexBestKnownBlock = NULL; - hashLastUnknownBlock.SetNull(); - pindexLastCommonBlock = NULL; - fSyncStarted = false; - nStallingSince = 0; - nBlocksInFlight = 0; - nBlocksInFlightValidHeaders = 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() -{ - LOCK(cs_main); - 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; -} - -// Returns time at which to timeout block request (nTime in microseconds) -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) -{ - return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); -} - -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); -} -void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) -{ -/* int expired = pool.Expire(GetTime() - age); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + struct CBlockReject { + unsigned char chRejectCode; + string strRejectReason; + uint256 hashBlock; + }; - std::vector vNoSpendsRemaining; - pool.TrimToSize(limit, &vNoSpendsRemaining); - BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) - pcoinsTip->Uncache(removed);*/ -} - -// Requires cs_main. -// Returns a bool indicating whether we requested this block. -bool 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->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; - state->vBlocksInFlight.erase(itInFlight->second.second); - state->nBlocksInFlight--; - state->nStallingSince = 0; - mapBlocksInFlight.erase(itInFlight); - return true; - } - return false; -} - -// Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure it's not listed somewhere already. - MarkBlockAsReceived(hash); - - int64_t nNow = GetTimeMicros(); - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; - list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); - state->nBlocksInFlight++; - state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; - 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.IsNull()) { - 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.SetNull(); + /** + * 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; + int nBlocksInFlightValidHeaders; + //! Whether we consider this a preferred download peer. + bool fPreferredDownload; + + CNodeState() { + fCurrentlyConnected = false; + nMisbehavior = 0; + fShouldBan = false; + pindexBestKnownBlock = NULL; + hashLastUnknownBlock.SetNull(); + pindexLastCommonBlock = NULL; + fSyncStarted = false; + nStallingSince = 0; + nBlocksInFlight = 0; + nBlocksInFlightValidHeaders = 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; } -} - -/** 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*/ + + int GetHeight() { - // An unknown block was announced; just assume that the latest one is the best one. - state->hashLastUnknownBlock = hash; + LOCK(cs_main); + return chainActive.Height(); } -} - -/** 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); + + 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; } - - while (pa != pb && pa && pb) { - pa = pa->pprev; - pb = pb->pprev; + + // Returns time at which to timeout block request (nTime in microseconds) + int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) + { + return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); } - - // 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; + + 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; } - - 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 its 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; + + void FinalizeNode(NodeId nodeid) { + LOCK(cs_main); + CNodeState *state = State(nodeid); + + if (state->fSyncStarted) + nSyncStarted--; + + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + AddressCurrentlyConnected(state->address); } - - // 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, or if it's - // already part of our chain (and therefore don't need it even if pruned). - BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { - if (!pindex->IsValid(BLOCK_VALID_TREE)) { - // We consider the chain that this peer is on invalid. - return; + + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) + mapBlocksInFlight.erase(entry.hash); + EraseOrphansFor(nodeid); + nPreferredDownload -= state->fPreferredDownload; + + mapNodeState.erase(nodeid); + } + + void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) + { + /* int expired = pool.Expire(GetTime() - age); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + std::vector vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed);*/ + } + + // Requires cs_main. + // Returns a bool indicating whether we requested this block. + bool 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->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; + state->vBlocksInFlight.erase(itInFlight->second.second); + state->nBlocksInFlight--; + state->nStallingSince = 0; + mapBlocksInFlight.erase(itInFlight); + return true; + } + return false; + } + + // Requires cs_main. + void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure it's not listed somewhere already. + MarkBlockAsReceived(hash); + + int64_t nNow = GetTimeMicros(); + QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; + nQueuedValidatedHeaders += newentry.fValidatedHeaders; + list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); + state->nBlocksInFlight++; + state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; + 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.IsNull()) { + 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.SetNull(); } - if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { - 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; + } + } + + /** 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 its 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 meantime, update + // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's + // already part of our chain (and therefore don't need it even if pruned). + 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 || chainActive.Contains(pindex)) { + 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; } - 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; } - 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 bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { @@ -558,6 +565,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc CBlockIndex* pindex = (*mi).second; if (pindex != 0 && chain.Contains(pindex)) return pindex; + if (pindex != 0 && pindex->GetAncestor(chain.Height()) == chain.Tip()) { + return chain.Tip(); + } } } return chain.Genesis(); @@ -581,7 +591,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c 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 @@ -595,12 +605,12 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c 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); - + 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; @@ -650,25 +660,31 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE map::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); - EraseOrphanTx(it->first); - ++nEvicted; + EraseOrphanTx(it->first); + ++nEvicted; } return nEvicted; } - - - - - -bool IsStandardTx(const CTransaction& tx, string& reason) +bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) { - if (tx.nVersion > CTransaction::MAX_CURRENT_VERSION || tx.nVersion < CTransaction::MIN_CURRENT_VERSION) { - reason = "version"; - return false; + bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); + + if (isOverwinter) { + // Overwinter standard rules apply + if (tx.nVersion > CTransaction::OVERWINTER_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::OVERWINTER_MIN_CURRENT_VERSION) { + reason = "overwinter-version"; + return false; + } + } else { + // Sprout standard rules apply + if (tx.nVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION) { + reason = "version"; + return false; + } } - + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed @@ -687,7 +703,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) return false; } } - + unsigned int v=0,nDataOut = 0; txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -713,13 +729,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason) } v++; } - + // only one OP_RETURN txout is permitted if (nDataOut > 1) { reason = "multi-op-return"; return false; } - + return true; } @@ -738,17 +754,25 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) } else if (!txin.IsFinal()) { - printf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); + //printf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); return false; } } return true; } +bool IsExpiredTx(const CTransaction &tx, int nBlockHeight) +{ + if (tx.nExpiryHeight == 0 || tx.IsCoinBase()) { + return false; + } + return static_cast(nBlockHeight) > tx.nExpiryHeight; +} + bool CheckFinalTx(const CTransaction &tx, int flags) { 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 @@ -756,7 +780,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // 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* @@ -764,15 +788,15 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // 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; - + // 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. // However this changes once median past time-locks are enforced: const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) - ? chainActive.Tip()->GetMedianTimePast() - : GetAdjustedTime(); - + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -785,15 +809,15 @@ bool CheckFinalTx(const CTransaction &tx, int flags) * 2. P2SH scripts with a crazy number of expensive * CHECKSIG/CHECKMULTISIG operations */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, uint32_t consensusBranchId) { if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally - + 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: @@ -803,7 +827,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) 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 @@ -811,9 +835,9 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // IsStandardTx() will have already returned false // and this method isn't called. vector > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId)) return false; - + if (whichType == TX_SCRIPTHASH) { if (stack.empty()) @@ -836,11 +860,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return (sigops <= MAX_P2SH_SIGOPS); } } - + if (stack.size() != (unsigned int)nArgsExpected) return false; } - + return true; } @@ -862,7 +886,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in { if (tx.IsCoinBase()) return 0; - + unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -873,7 +897,80 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } -bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash::ProofVerifier& verifier) +/** + * Check a transaction contextually against a set of consensus rules valid at a given block height. + * + * Notes: + * 1. AcceptToMemoryPool calls CheckTransaction and this function. + * 2. ProcessNewBlock calls AcceptBlock, which calls CheckBlock (which calls CheckTransaction) + * and ContextualCheckBlock (which calls this function). + */ +bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, const int nHeight, const int dosLevel) +{ + bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); + bool isSprout = !isOverwinter; + + // If Sprout rules apply, reject transactions which are intended for Overwinter and beyond + if (isSprout && tx.fOverwintered) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter is not active yet"), + REJECT_INVALID, "tx-overwinter-not-active"); + } + + // If Overwinter rules apply: + if (isOverwinter) { + // Reject transactions with valid version but missing overwinter flag + if (tx.nVersion >= OVERWINTER_MIN_TX_VERSION && !tx.fOverwintered) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter flag must be set"), + REJECT_INVALID, "tx-overwinter-flag-not-set"); + } + + // Reject transactions with invalid version + if (tx.fOverwintered && tx.nVersion > OVERWINTER_MAX_TX_VERSION ) { + return state.DoS(100, error("CheckTransaction(): overwinter version too high"), + REJECT_INVALID, "bad-tx-overwinter-version-too-high"); + } + + // Reject transactions intended for Sprout + if (!tx.fOverwintered) { + return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"), + REJECT_INVALID, "tx-overwinter-active"); + } + + // Check that all transactions are unexpired + if (IsExpiredTx(tx, nHeight)) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); + } + } + + if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) { + auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); + // Empty output script. + CScript scriptCode; + uint256 dataToBeSigned; + try { + dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); + } catch (std::logic_error ex) { + return state.DoS(100, error("CheckTransaction(): error computing signature hash"), + REJECT_INVALID, "error-computing-signature-hash"); + } + + BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); + + // We rely on libsodium to check that the signature is canonical. + // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 + if (crypto_sign_verify_detached(&tx.joinSplitSig[0], + dataToBeSigned.begin(), 32, + tx.joinSplitPubKey.begin() + ) != 0) { + return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), + REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); + } + } + return true; +} + +bool CheckTransaction(const CTransaction& tx, CValidationState &state, + libzcash::ProofVerifier& verifier) { static uint256 array[64]; static int32_t numbanned,indallvouts; int32_t j,k,n; if ( *(int32_t *)&array[0] == 0 ) @@ -892,11 +989,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash:: } } } - // Don't count coinbase transactions because mining skews the count + // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { transactionsValidated.increment(); } - + if (!CheckTransactionWithoutProofVerification(tx, state)) { return false; } else { @@ -904,7 +1001,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash:: BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), - REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); + REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); } } return true; @@ -914,13 +1011,48 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash:: bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - - // Check transaction version - if (tx.nVersion < MIN_TX_VERSION) { + + /** + * Previously: + * 1. The consensus rule below was: + * if (tx.nVersion < SPROUT_MIN_TX_VERSION) { ... } + * which checked if tx.nVersion fell within the range: + * INT32_MIN <= tx.nVersion < SPROUT_MIN_TX_VERSION + * 2. The parser allowed tx.nVersion to be negative + * + * Now: + * 1. The consensus rule checks to see if tx.Version falls within the range: + * 0 <= tx.nVersion < SPROUT_MIN_TX_VERSION + * 2. The previous consensus rule checked for negative values within the range: + * INT32_MIN <= tx.nVersion < 0 + * This is unnecessary for Overwinter transactions since the parser now + * interprets the sign bit as fOverwintered, so tx.nVersion is always >=0, + * and when Overwinter is not active ContextualCheckTransaction rejects + * transactions with fOverwintered set. When fOverwintered is set, + * this function and ContextualCheckTransaction will together check to + * ensure tx.nVersion avoids the following ranges: + * 0 <= tx.nVersion < OVERWINTER_MIN_TX_VERSION + * OVERWINTER_MAX_TX_VERSION < tx.nVersion <= INT32_MAX + */ + if (!tx.fOverwintered && tx.nVersion < SPROUT_MIN_TX_VERSION) { return state.DoS(100, error("CheckTransaction(): version too low"), REJECT_INVALID, "bad-txns-version-too-low"); } - + else if (tx.fOverwintered) { + if (tx.nVersion < OVERWINTER_MIN_TX_VERSION) { + return state.DoS(100, error("CheckTransaction(): overwinter version too low"), + REJECT_INVALID, "bad-tx-overwinter-version-too-low"); + } + if (tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) { + return state.DoS(100, error("CheckTransaction(): unknown tx version group id"), + REJECT_INVALID, "bad-tx-version-group-id"); + } + if (tx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) { + return state.DoS(100, error("CheckTransaction(): expiry height is too high"), + REJECT_INVALID, "bad-tx-expiry-height-too-high"); + } + } + // Transactions can contain empty `vin` and `vout` so long as // `vjoinsplit` is non-empty. if (tx.vin.empty() && tx.vjoinsplit.empty()) @@ -929,13 +1061,13 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio if (tx.vout.empty() && tx.vjoinsplit.empty()) return state.DoS(10, error("CheckTransaction(): vout empty"), REJECT_INVALID, "bad-txns-vout-empty"); - + // Size limits BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE > MAX_TX_SIZE); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE) return state.DoS(100, error("CheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); - + // Check for negative or overflow output values CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -953,7 +1085,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } - + // Ensure that joinsplit values are well-formed BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { @@ -961,34 +1093,34 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old negative"), REJECT_INVALID, "bad-txns-vpub_old-negative"); } - + if (joinsplit.vpub_new < 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new negative"), REJECT_INVALID, "bad-txns-vpub_new-negative"); } - + if (joinsplit.vpub_old > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old too high"), REJECT_INVALID, "bad-txns-vpub_old-toolarge"); } - + if (joinsplit.vpub_new > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new too high"), REJECT_INVALID, "bad-txns-vpub_new-toolarge"); } - + if (joinsplit.vpub_new != 0 && joinsplit.vpub_old != 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new and joinsplit.vpub_old both nonzero"), REJECT_INVALID, "bad-txns-vpubs-both-nonzero"); } - + nValueOut += joinsplit.vpub_old; if (!MoneyRange(nValueOut)) { return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } } - + // Ensure input values do not exceed MAX_MONEY // We have not resolved the txin values at this stage, // but we do know what the joinsplits claim to add @@ -998,15 +1130,15 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio for (std::vector::const_iterator it(tx.vjoinsplit.begin()); it != tx.vjoinsplit.end(); ++it) { nValueIn += it->vpub_new; - + if (!MoneyRange(it->vpub_new) || !MoneyRange(nValueIn)) { return state.DoS(100, error("CheckTransaction(): txin total out of range"), REJECT_INVALID, "bad-txns-txintotal-toolarge"); } } } - - + + // Check for duplicate inputs set vInOutPoints; BOOST_FOREACH(const CTxIn& txin, tx.vin) @@ -1016,7 +1148,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } - + // Check for duplicate joinsplit nullifiers in this transaction set vJoinSplitNullifiers; BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) @@ -1025,19 +1157,19 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio { if (vJoinSplitNullifiers.count(nf)) return state.DoS(100, error("CheckTransaction(): duplicate nullifiers"), - REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate"); - + REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate"); + vJoinSplitNullifiers.insert(nf); } } - + if (tx.IsCoinBase()) { // There should be no joinsplits in a coinbase transaction if (tx.vjoinsplit.size() > 0) return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"), REJECT_INVALID, "bad-cb-has-joinsplits"); - + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, error("CheckTransaction(): coinbase script size"), REJECT_INVALID, "bad-cb-length"); @@ -1045,35 +1177,11 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio else { BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); - - if (tx.vjoinsplit.size() > 0) { - // Empty output script. - CScript scriptCode; - uint256 dataToBeSigned; - try { - dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL); - } catch (std::logic_error ex) { - return state.DoS(100, error("CheckTransaction(): error computing signature hash"), - REJECT_INVALID, "error-computing-signature-hash"); - } - - BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); - - // We rely on libsodium to check that the signature is canonical. - // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 - if (crypto_sign_verify_detached(&tx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - tx.joinSplitPubKey.begin() - ) != 0) { - return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), - REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); - } - } + if (txin.prevout.IsNull()) + return state.DoS(10, error("CheckTransaction(): prevout is null"), + REJECT_INVALID, "bad-txns-prevout-null"); } - + return true; } @@ -1089,9 +1197,9 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (dPriorityDelta > 0 || nFeeDelta > 0) return 0; } - + CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - + if (fAllowFree) { // There is a free transaction area in blocks created by most miners, @@ -1101,7 +1209,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) nMinFee = 0; } - + if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; @@ -1113,6 +1221,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; + + int nextBlockHeight = chainActive.Height() + 1; + auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus()); + + // Node operator can choose to reject tx by number of transparent inputs + static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "size_t too small"); + size_t limit = (size_t) GetArg("-mempooltxinputlimit", 0); + if (limit > 0) { + size_t n = tx.vin.size(); + if (n > limit) { + LogPrint("mempool", "Dropping txid %s : too many transparent inputs %zu > limit %zu\n", tx.GetHash().ToString(), n, limit ); + return false; + } + } + auto verifier = libzcash::ProofVerifier::Strict(); if ( komodo_validate_interest(tx,chainActive.Tip()->nHeight+1,chainActive.Tip()->GetMedianTimePast() + 777,0) < 0 ) { @@ -1121,9 +1244,16 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } if (!CheckTransaction(tx, state, verifier)) { - fprintf(stderr,"accept failure.0\n"); + return error("AcceptToMemoryPool: CheckTransaction failed"); } + // DoS level set to 10 to be more forgiving. + // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. + if (!ContextualCheckTransaction(tx, state, nextBlockHeight, 10)) + { + return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); + } + // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) { @@ -1132,9 +1262,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; - if (Params().RequireStandard() && !IsStandardTx(tx, reason)) + if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) { - fprintf(stderr,"AcceptToMemoryPool nonstandard transaction: %s\n",reason.c_str()); + fprintf(stderr,"AcceptToMemoryPool reject nonstandard transaction: %s\n",reason.c_str()); return state.DoS(0,error("AcceptToMemoryPool: nonstandard transaction: %s", reason),REJECT_NONSTANDARD, reason); } // Only accept nLockTime-using transactions that can be mined in the next @@ -1145,99 +1275,100 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa //fprintf(stderr,"AcceptToMemoryPool reject non-final\n"); return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); } - // is it already in the memory pool? + // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) { fprintf(stderr,"already in mempool\n"); return false; } - + // Check for conflicts with in-memory transactions { - 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)) + LOCK(pool.cs); // protect pool.mapNextTx + for (unsigned int i = 0; i < tx.vin.size(); i++) { - static uint32_t counter; - // Disable replacement feature for now - //if ( counter++ < 100 ) - fprintf(stderr,"Disable replacement feature for now\n"); - return false; - } - } - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { - BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { - if (pool.mapNullifiers.count(nf)) + COutPoint outpoint = tx.vin[i].prevout; + if (pool.mapNextTx.count(outpoint)) { - fprintf(stderr,"pool.mapNullifiers.count\n"); + static uint32_t counter; + // Disable replacement feature for now + //if ( counter++ < 100 ) + fprintf(stderr,"Disable replacement feature for now\n"); return false; } } + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) + { + BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) + { + if (pool.mapNullifiers.count(nf)) + { + fprintf(stderr,"pool.mapNullifiers.count\n"); + return false; + } + } + } } - } - + { CCoinsView dummy; CCoinsViewCache view(&dummy); int64_t interest; CAmount nValueIn = 0; { - LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); - view.SetBackend(viewMemPool); - - // do we already have it? - if (view.HaveCoins(hash)) - { - fprintf(stderr,"view.HaveCoins(hash) error\n"); - return false; - } - - // do all inputs exist? - // Note that this does not check for the presence of actual outputs (see the next check for that), - // and only helps with filling in pfMissingInputs (to determine missing vs spent). - BOOST_FOREACH(const CTxIn txin, tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; - //fprintf(stderr,"missing inputs\n"); + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + view.SetBackend(viewMemPool); + + // do we already have it? + if (view.HaveCoins(hash)) + { + fprintf(stderr,"view.HaveCoins(hash) error\n"); return false; } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - { - //fprintf(stderr,"accept failure.1\n"); - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); - } - // are the joinsplit's requirements met? - if (!view.HaveJoinSplitRequirements(tx)) - { - fprintf(stderr,"accept failure.2\n"); - return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); - } - - // Bring the best block into scope - view.GetBestBlock(); - - nValueIn = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime); + + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // and only helps with filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) + { + if (!view.HaveCoins(txin.prevout.hash)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + //fprintf(stderr,"missing inputs\n"); + return false; + } + } + + // are the actual inputs available? + if (!view.HaveInputs(tx)) + { + //fprintf(stderr,"accept failure.1\n"); + return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); + } + // are the joinsplit's requirements met? + if (!view.HaveJoinSplitRequirements(tx)) + { + //fprintf(stderr,"accept failure.2\n"); + return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); + } + + // Bring the best block into scope + view.GetBestBlock(); + + nValueIn = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime); if ( 0 && interest != 0 ) fprintf(stderr,"add interest %.8f\n",(double)interest/COIN); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); + // 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)) - { - fprintf(stderr,"accept failure.3\n"); - return error("AcceptToMemoryPool: nonstandard transaction input"); - } - + if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) + return error("AcceptToMemoryPool: reject 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_STANDARD_TX_SIGOPS is less than @@ -1250,14 +1381,30 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa fprintf(stderr,"accept failure.4\n"); return state.DoS(0, error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),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(), mempool.HasNoInputsOf(tx)); + + // Keep track of transactions that spend a coinbase, which we re-scan + // during reorgs to ensure COINBASE_MATURITY is still met. + bool fSpendsCoinbase = false; + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + const CCoins *coins = view.AccessCoins(txin.prevout.hash); + if (coins->IsCoinBase()) { + fSpendsCoinbase = true; + break; + } + } + + // Grab the branch ID we expect this transaction to commit to. We don't + // yet know if it does, but if the entry gets added to the mempool, then + // it has passed ContextualCheckInputs and therefore this is correct. + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); - + // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. if (tx.vjoinsplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) { // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits. @@ -1266,17 +1413,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount txMinFee = GetMinRelayFee(tx, nSize, true); if (fLimitFree && nFees < txMinFee) { - fprintf(stderr,"accept failure.5\n"); + //fprintf(stderr,"accept failure.5\n"); 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 (GetBoolArg("-relaypriority", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { fprintf(stderr,"accept failure.6\n"); 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. @@ -1286,9 +1433,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa 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; @@ -1302,21 +1449,22 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } - + if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19 ) { fprintf(stderr,"accept failure.8\n"); return error("AcceptToMemoryPool: absurdly high 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 (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + PrecomputedTransactionData txdata(tx); + if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { - fprintf(stderr,"accept failure.9\n"); + //fprintf(stderr,"accept failure.9\n"); 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 @@ -1326,20 +1474,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // 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 (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { fprintf(stderr,"accept failure.10\n"); return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); } - + // Store transaction in memory if ( komodo_is_notarytx(tx) == 0 ) KOMODO_ON_DEMAND++; pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); } - + SyncWithWallets(tx, NULL); - + return true; } @@ -1347,14 +1495,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa 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)) { @@ -1375,7 +1523,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock return true; } } - + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { @@ -1387,7 +1535,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (nHeight > 0) pindexSlow = chainActive[nHeight]; } - + if (pindexSlow) { CBlock block; if (ReadBlockFromDisk(block, pindexSlow)) { @@ -1400,23 +1548,23 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock } } } - + return false; } /*char *komodo_getspendscript(uint256 hash,int32_t n) -{ - CTransaction tx; uint256 hashBlock; - if ( !GetTransaction(hash,tx,hashBlock,true) ) - { - printf("null GetTransaction\n"); - return(0); - } - if ( n >= 0 && n < tx.vout.size() ) - return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); - else printf("getspendscript illegal n.%d\n",n); - return(0); -}*/ + { + CTransaction tx; uint256 hashBlock; + if ( !GetTransaction(hash,tx,hashBlock,true) ) + { + printf("null GetTransaction\n"); + return(0); + } + if ( n >= 0 && n < tx.vout.size() ) + return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); + else printf("getspendscript illegal n.%d\n",n); + return(0); + }*/ ////////////////////////////////////////////////////////////////////////////// @@ -1430,18 +1578,18 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M 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(messageStart) << nSize; - + // Write block long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) return error("WriteBlockToDisk: ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << block; - + return true; } @@ -1449,7 +1597,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) { uint8_t pubkey33[33]; block.SetNull(); - + // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) @@ -1457,7 +1605,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) //fprintf(stderr,"readblockfromdisk err A\n"); return false;//error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); } - + // Read block try { filein >> block; @@ -1471,7 +1619,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()))) { int32_t i; for (i=0; i<33; i++) - printf("%02x",pubkey33[i]); + fprintf(stderr,"%02x",pubkey33[i]); fprintf(stderr," warning unexpected diff at ht.%d\n",height); return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); @@ -1487,18 +1635,19 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return false; if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", - pindex->ToString(), pindex->GetBlockPos().ToString()); + pindex->ToString(), pindex->GetBlockPos().ToString()); return true; } //uint64_t komodo_moneysupply(int32_t height); extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_SUPPLY; +extern uint64_t ASSETCHAINS_STAKED,ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { - CAmount nSubsidy = 3 * COIN; + static uint64_t cached_subsidy; static int32_t cached_numhalvings; + int32_t numhalvings,i; uint64_t numerator; CAmount nSubsidy = 3 * COIN; if ( ASSETCHAINS_SYMBOL[0] == 0 ) { if ( nHeight == 1 ) @@ -1511,29 +1660,67 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { if ( nHeight == 1 ) return(ASSETCHAINS_SUPPLY * COIN + (ASSETCHAINS_MAGIC & 0xffffff)); - else return(10000); + else if ( ASSETCHAINS_ENDSUBSIDY == 0 || nHeight < ASSETCHAINS_ENDSUBSIDY ) + { + if ( ASSETCHAINS_REWARD == 0 ) + return(10000); + else if ( ASSETCHAINS_ENDSUBSIDY != 0 && nHeight >= ASSETCHAINS_ENDSUBSIDY ) + return(0); + else + { + nSubsidy = ASSETCHAINS_REWARD; + if ( ASSETCHAINS_HALVING != 0 ) + { + if ( (numhalvings= (nHeight / ASSETCHAINS_HALVING)) > 0 ) + { + if ( numhalvings >= 64 && ASSETCHAINS_DECAY == 0 ) + return(0); + if ( ASSETCHAINS_DECAY == 0 ) + nSubsidy >>= numhalvings; + else if ( ASSETCHAINS_DECAY == 100000000 && ASSETCHAINS_ENDSUBSIDY != 0 ) + { + numerator = (ASSETCHAINS_ENDSUBSIDY - nHeight); + nSubsidy = (nSubsidy * numerator) / ASSETCHAINS_ENDSUBSIDY; + } + else + { + if ( cached_subsidy > 0 && cached_numhalvings == numhalvings ) + nSubsidy = cached_subsidy; + else + { + for (i=0; i consensusParams.SubsidySlowStartShift()); - int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ + /* + // Mining slow start + // The subsidy is ramped up linearly, skipping the middle payout of + // MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start. + if (nHeight < consensusParams.nSubsidySlowStartInterval / 2) { + nSubsidy /= consensusParams.nSubsidySlowStartInterval; + nSubsidy *= nHeight; + return nSubsidy; + } else if (nHeight < consensusParams.nSubsidySlowStartInterval) { + nSubsidy /= consensusParams.nSubsidySlowStartInterval; + nSubsidy *= (nHeight+1); + return nSubsidy; + } + + assert(nHeight > consensusParams.SubsidySlowStartShift()); + int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ // Force block reward to zero when right shift is undefined. //int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; //if (halvings >= 64) // return 0; - + // Subsidy is cut in half every 840,000 blocks which will occur approximately every 4 years. //nSubsidy >>= halvings; return nSubsidy; @@ -1564,10 +1751,10 @@ bool IsInitialBlockDownload() ptr = pindexBestHeader; else if ( pindexBestHeader != 0 && pindexBestHeader->nHeight > ptr->nHeight ) ptr = pindexBestHeader; - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - state = ((chainActive.Height() < ptr->nHeight - 24*60) || - ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); - else state = (chainActive.Height() < ptr->nHeight - 10); + //if ( ASSETCHAINS_SYMBOL[0] == 0 ) + state = ((chainActive.Height() < ptr->nHeight - 24*60) || + ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); + //else state = (chainActive.Height() < ptr->nHeight - 24*60); //fprintf(stderr,"state.%d ht.%d vs %d, t.%u %u\n",state,(int32_t)chainActive.Height(),(uint32_t)ptr->nHeight,(int32_t)ptr->GetBlockTime(),(uint32_t)(GetTime() - chainParams.MaxTipAge())); if (!state) { @@ -1587,25 +1774,25 @@ void CheckForkWarningConditions() // (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 288 blocks (+/- 12 hours if no one mines it) // of our head, drop it if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 288) pindexBestForkTip = NULL; - + if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { if (!fLargeWorkForkFound && pindexBestForkBase) { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); + pindexBestForkBase->phashBlock->ToString() + std::string("'"); CAlert::Notify(warning, true); } if (pindexBestForkTip && pindexBestForkBase) { LogPrintf("%s: 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", __func__, - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); fLargeWorkForkFound = true; } else @@ -1637,7 +1824,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) break; pfork = pfork->pprev; } - + // We define a condition where we should warn the user about as a fork of at least 7 blocks // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network @@ -1646,13 +1833,13 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // 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) + pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && + chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; } - + CheckForkWarningConditions(); } @@ -1661,13 +1848,13 @@ void Misbehaving(NodeId pnode, int howmuch) { if (howmuch == 0) return; - + CNodeState *state = State(pnode); if (state == NULL) return; - + state->nMisbehavior += howmuch; - int banscore = GetArg("-banscore", 100); + int banscore = GetArg("-banscore", 101); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); @@ -1680,16 +1867,16 @@ void static InvalidChainFound(CBlockIndex* pindexNew) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; - + LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, - pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime())); + pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", + pindexNew->GetBlockTime())); CBlockIndex *tip = chainActive.Tip(); assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, - tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); + tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); CheckForkWarningConditions(); } @@ -1712,7 +1899,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state } } -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) { if (!tx.IsCoinBase()) // mark inputs spent { @@ -1720,7 +1907,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); unsigned nPos = txin.prevout.n; - + if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) assert(false); // mark an outpoint spent, and construct undo information @@ -1742,15 +1929,15 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs } -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) { CTxUndo txundo; - UpdateCoins(tx, state, inputs, txundo, nHeight); + UpdateCoins(tx, inputs, txundo, nHeight); } bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, ServerTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; @@ -1764,17 +1951,17 @@ int GetSpendHeight(const CCoinsViewCache& inputs) } namespace Consensus { -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) -{ + bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) + { // 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())); - + // are the JoinSplit's requirements met? if (!inputs.HaveJoinSplitRequirements(tx)) return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString())); - + CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1782,30 +1969,30 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins const COutPoint &prevout = tx.vin[i].prevout; const CCoins *coins = inputs.AccessCoins(prevout.hash); assert(coins); - + if (coins->IsCoinBase()) { // Ensure that coinbases are matured if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } - + // Ensure that coinbases cannot be spent to transparent outputs // Disabled on regtest if (fCoinbaseEnforcedProtectionEnabled && consensusParams.fCoinbaseMustBeProtected && !tx.vout.empty()) { return state.Invalid( - error("CheckInputs(): tried to spend coinbase with transparent outputs"), - REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); + error("CheckInputs(): tried to spend coinbase with transparent outputs"), + REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); } } - + // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; #ifdef KOMODO_ENABLE_INTEREST - if ( ASSETCHAINS_SYMBOL[0] == 0 && chainActive.Tip() != 0 && chainActive.Tip()->nHeight >= 60000 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 && nSpendHeight > 60000 )//chainActive.Tip() != 0 && chainActive.Tip()->nHeight >= 60000 ) { if ( coins->vout[prevout.n].nValue >= 10*COIN ) { @@ -1821,18 +2008,20 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins 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"); - + } - + nValueIn += tx.GetJoinSplitValueIn(); if (!MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): vpub_old values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + if (nValueIn < tx.GetValueOut()) + { + fprintf(stderr,"spentheight.%d valuein %s vs %s error\n",nSpendHeight,FormatMoney(nValueIn).c_str(), FormatMoney(tx.GetValueOut()).c_str()); return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s) diff %.8f", tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()),((double)nValueIn - tx.GetValueOut())/COIN),REJECT_INVALID, "bad-txns-in-belowout"); - + } // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) @@ -1842,24 +2031,35 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs(): nFees out of range"), REJECT_INVALID, "bad-txns-fee-outofrange"); - return true; -} + return true; + } }// namespace Consensus -bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector *pvChecks) +bool ContextualCheckInputs( + const CTransaction& tx, + CValidationState &state, + const CCoinsViewCache &inputs, + bool fScriptChecks, + unsigned int flags, + bool cacheStore, + PrecomputedTransactionData& txdata, + const Consensus::Params& consensusParams, + uint32_t consensusBranchId, + std::vector *pvChecks) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) - return false; - if (!tx.IsCoinBase()) { + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) { + return false; + } + if (pvChecks) pvChecks->reserve(tx.vin.size()); - + // 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. @@ -1868,9 +2068,9 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons 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); + CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1882,9 +2082,9 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons // 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()) + CScriptCheck check2(*coins, tx, i, + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, &txdata); + if (check2()) 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 @@ -1899,123 +2099,123 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons } } } - + return true; } /*bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector *pvChecks) -{ - if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { - fprintf(stderr,"ContextualCheckInputs failure.0\n"); - return false; - } - - if (!tx.IsCoinBase()) - { - // 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; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins *coins = inputs.AccessCoins(prevout.hash); - // Assertion is okay because NonContextualCheckInputs ensures the inputs - // are available. - assert(coins); - - // If prev is coinbase, check that it's matured - if (coins->IsCoinBase()) { - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - COINBASE_MATURITY = _COINBASE_MATURITY; - if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { - fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); - - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); - } - } - } - } - - return true; -}*/ + { + if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { + fprintf(stderr,"ContextualCheckInputs failure.0\n"); + return false; + } + + if (!tx.IsCoinBase()) + { + // 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; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins *coins = inputs.AccessCoins(prevout.hash); + // Assertion is okay because NonContextualCheckInputs ensures the inputs + // are available. + assert(coins); + + // If prev is coinbase, check that it's matured + if (coins->IsCoinBase()) { + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + COINBASE_MATURITY = _COINBASE_MATURITY; + if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { + fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); + + return state.Invalid( + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + } + } + } + } + + return true; + }*/ namespace { - -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) -{ - // Open history file to append - CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: OpenUndoFile failed", __func__); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(messageStart) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("%s: ftell failed", __func__); - pos.nPos = (unsigned int)fileOutPos; - fileout << blockundo; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - fileout << hasher.GetHash(); - - return true; -} - -bool UndoReadFromDisk(CBlockUndo& blockundo, 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("%s: OpenBlockFile failed", __func__); - - // Read block - uint256 hashChecksum; - try { - filein >> blockundo; - filein >> hashChecksum; + + bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) + { + // Open history file to append + CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: OpenUndoFile failed", __func__); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(blockundo); + fileout << FLATDATA(messageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("%s: ftell failed", __func__); + pos.nPos = (unsigned int)fileOutPos; + fileout << blockundo; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + fileout << hasher.GetHash(); + + return true; } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + + bool UndoReadFromDisk(CBlockUndo& blockundo, 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("%s: OpenBlockFile failed", __func__); + + // Read block + uint256 hashChecksum; + try { + filein >> blockundo; + filein >> hashChecksum; + } + catch (const 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 << blockundo; + if (hashChecksum != hasher.GetHash()) + return error("%s: Checksum mismatch", __func__); + + return true; } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - if (hashChecksum != hasher.GetHash()) - return error("%s: Checksum mismatch", __func__); - - return true; -} - -/** Abort with a message */ -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 occurred, see debug.log for details") : userMessage, - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; -} - -bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") -{ - AbortNode(strMessage, userMessage); - return state.Error(strMessage); -} - + + /** Abort with a message */ + 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 occurred, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + + bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") + { + AbortNode(strMessage, userMessage); + return state.Error(strMessage); + } + } // anon namespace /** @@ -2028,7 +2228,7 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) { bool fClean = true; - + 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 @@ -2047,17 +2247,17 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO if (coins->vout.size() < out.n+1) coins->vout.resize(out.n+1); coins->vout[out.n] = undo.txout; - + return fClean; } bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); - + if (pfClean) *pfClean = false; - + bool fClean = true; komodo_disconnect(pindex,block); CBlockUndo blockUndo; @@ -2066,41 +2266,41 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex return error("DisconnectBlock(): no undo data available"); if (!UndoReadFromDisk(blockUndo, 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]; uint256 hash = tx.GetHash(); - + // Check that all outputs are available and match the outputs in the block itself // exactly. { - 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(); + 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(); } - + // unspend nullifiers BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { view.SetNullifier(nf, false); } } - + // restore inputs if (i > 0) { // not coinbases const CTxUndo &txundo = blockUndo.vtxundo[i-1]; @@ -2114,27 +2314,27 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } } - + // set the old best anchor back view.PopAnchor(blockUndo.old_tree_root); - + // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); - + if (pfClean) { *pfClean = fClean; return true; } - + return fClean; } void static FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); - + CDiskBlockPos posOld(nLastBlockFile, 0); - + FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) @@ -2142,7 +2342,7 @@ void static FlushBlockFile(bool fFinalize = false) FileCommit(fileOld); fclose(fileOld); } - + fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) @@ -2170,39 +2370,39 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const int64_t nPowTargetSpacing) { if (bestHeader == NULL || initialDownloadCheck()) return; - + static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); if (lastAlertTime > now-60*60*24) return; // Alert at most once per day - + const int SPAN_HOURS=4; const int SPAN_SECONDS=SPAN_HOURS*60*60; int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; - + boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); - + std::string strWarning; int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; - + LOCK(cs); const CBlockIndex* i = bestHeader; int nBlocks = 0; while (i->GetBlockTime() >= startTime) { ++nBlocks; i = i->pprev; - if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed + if (i == NULL) return; // Ran out of chain, we must not be fully synced } - + // How likely is it to find that many by chance? double p = boost::math::pdf(poisson, nBlocks); - + LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS); LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p); - + // Aim for one false-positive about every fifty years of normal running: const int FIFTY_YEARS = 50*365*24*60*60; double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); - + if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) { // Many fewer blocks than expected: alert! @@ -2232,13 +2432,9 @@ static int64_t nTimeTotal = 0; bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { const CChainParams& chainparams = Params(); + //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->nHeight); AssertLockHeld(cs_main); -/*<<<<<<< HEA - // Check it again in case a previous version let a bad block in - bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); -======= -*/ bool fExpensiveChecks = true; if (fCheckpointsEnabled) { CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); @@ -2247,18 +2443,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin fExpensiveChecks = false; } } -//>>>>>>> zcash/master auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); - + // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck)) return false; - + // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); assert(hashPrevBlock == view.GetBestBlock()); - + // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { @@ -2272,7 +2467,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } return true; } - + bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); //if ( KOMODO_TESTNET_EXPIRATION != 0 && pindex->nHeight > KOMODO_TESTNET_EXPIRATION ) // "testnet" // return(false); @@ -2284,25 +2479,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); } - - unsigned int flags = SCRIPT_VERIFY_P2SH; - - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, - // when 75% of the network has upgraded: - if (block.nVersion >= 3) { - flags |= SCRIPT_VERIFY_DERSIG; - } - - // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4 - // blocks, when 75% of the network has upgraded: - if (block.nVersion >= 4) { - flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - } - + + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + + // DERSIG (BIP66) is also always enforced, but does not have a flag. + CBlockUndo blockundo; - + CCheckQueueControl control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - + int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; @@ -2312,7 +2497,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); - + // Construct the incremental merkle tree at the current // block position, auto old_tree_root = view.GetBestAnchor(); @@ -2324,13 +2509,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // This should never fail: we should always be able to get the root // that is on the tip of our chain assert(view.GetAnchorAt(old_tree_root, tree)); - + { // Consistency check: the root of the tree we're given should // match what we asked for. assert(tree.root() == old_tree_root); } - + + // Grab the consensus branch ID for the block's height + auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus()); + + std::vector txdata; + txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; @@ -2339,18 +2529,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); -//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); + //fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); - + // are the JoinSplit's requirements met? if (!view.HaveJoinSplitRequirements(tx)) return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); - + // 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. @@ -2358,11 +2548,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); - + } + + txdata.emplace_back(tx); + + if (!tx.IsCoinBase()) + { nFees += view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime) - tx.GetValueOut(); sum += interest; + std::vector vChecks; - if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL)) + if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } @@ -2372,45 +2568,59 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); } - UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); - + UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 ¬e_commitment, joinsplit.commitments) { // Insert the note commitments into our temporary tree. - + tree.append(note_commitment); } } - + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - + view.PushAnchor(tree); if (!fJustCheck) { pindex->hashAnchorEnd = tree.root(); } blockundo.old_tree_root = old_tree_root; - + 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); - - CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); - if (block.vtx[0].vout[0].nValue > blockReward) - //if (block.vtx[0].GetValueOut() > blockReward) - return state.DoS(100, - error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0].GetValueOut(), blockReward), - REJECT_INVALID, "bad-cb-amount"); - + + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()) + sum; + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && block.vtx[0].vout.size() > 1 ) + { + uint64_t checktoshis; + if ( (checktoshis = komodo_commission(block)) != 0 ) + { + if ( block.vtx[0].vout.size() == 2 && block.vtx[0].vout[1].nValue == checktoshis ) + blockReward += checktoshis; + else fprintf(stderr,"checktoshis %.8f vs actual vout[1] %.8f\n",dstr(checktoshis),dstr(block.vtx[0].vout[1].nValue)); + } + } + if ( block.vtx[0].GetValueOut() > blockReward+1 ) + { + if ( ASSETCHAINS_SYMBOL[0] != 0 || pindex->nHeight >= KOMODO_NOTARIES_HEIGHT1 || block.vtx[0].vout[0].nValue > blockReward ) + { + return state.DoS(100, + error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", + block.vtx[0].GetValueOut(), blockReward), + REJECT_INVALID, "bad-cb-amount"); + } else if ( NOTARY_PUBKEY33[0] != 0 ) + fprintf(stderr,"allow nHeight.%d coinbase %.8f vs %.8f interest %.8f\n",(int32_t)pindex->nHeight,dstr(block.vtx[0].GetValueOut()),dstr(blockReward),dstr(sum)); + } 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)) { @@ -2420,31 +2630,42 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return error("ConnectBlock(): FindUndoPos failed"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); - + // update nUndoPos in block index pindex->nUndoPos = pos.nPos; pindex->nStatus |= BLOCK_HAVE_UNDO; } - + + // Now that all consensus rules have been validated, set nCachedBranchId. + // Move this if BLOCK_VALID_CONSENSUS is ever altered. + static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS, + "nCachedBranchId must be set after all consensus rules have been validated."); + if (IsActivationHeightForAnyUpgrade(pindex->nHeight, Params().GetConsensus())) { + pindex->nStatus |= BLOCK_ACTIVATES_UPGRADE; + pindex->nCachedBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus()); + } else if (pindex->pprev) { + pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; + } + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); setDirtyBlockIndex.insert(pindex); } - + if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "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; GetMainSignals().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); @@ -2474,88 +2695,88 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune); - fCheckForPruning = false; - if (!setFilesToPrune.empty()) { - fFlushForPrune = true; - if (!fHavePruned) { - pblocktree->WriteFlag("prunedblockfiles", true); - fHavePruned = true; + if (fPruneMode && fCheckForPruning && !fReindex) { + FindFilesToPrune(setFilesToPrune); + fCheckForPruning = false; + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } } } - } - int64_t nNow = GetTimeMicros(); - // Avoid writing/flushing immediately after startup. - if (nLastWrite == 0) { - nLastWrite = nNow; - } - if (nLastFlush == 0) { - nLastFlush = nNow; - } - if (nLastSetChain == 0) { - nLastSetChain = nNow; - } - size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); - // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). - bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; - // The cache is over the limit, we have to write now. - bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; - // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. - bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; - // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. - bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; - // Combine all conditions that result in a full cache flush. - bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; - // Write blocks and block index to disk. - if (fDoFullFlush || fPeriodicWrite) { - // Depend on nMinDiskSpace to ensure we can write block index - if (!CheckDiskSpace(0)) - 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). - { - std::vector > vFiles; - vFiles.reserve(setDirtyFileInfo.size()); - for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); - setDirtyFileInfo.erase(it++); - } - std::vector vBlocks; - vBlocks.reserve(setDirtyBlockIndex.size()); - for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { - vBlocks.push_back(*it); - setDirtyBlockIndex.erase(it++); - } - if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Files to write to block index database"); - } + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); + // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) + 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). + { + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { + return AbortNode(state, "Files to write to block index database"); + } + } + // Finally remove any pruned files + if (fFlushForPrune) + UnlinkPrunedFiles(setFilesToPrune); + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Typical CCoins structures on disk are around 128 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(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return AbortNode(state, "Failed to write to coin database"); + nLastFlush = nNow; + } + if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { + // Update best block in wallet (so we can detect restored wallets). + GetMainSignals().SetBestChain(chainActive.GetLocator()); + nLastSetChain = nNow; } - // Finally remove any pruned files - if (fFlushForPrune) - UnlinkPrunedFiles(setFilesToPrune); - nLastWrite = nNow; - } - // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { - // Typical CCoins structures on disk are around 128 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(128 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error("out of disk space"); - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return AbortNode(state, "Failed to write to coin database"); - nLastFlush = nNow; - } - if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { - // Update best block in wallet (so we can detect restored wallets). - GetMainSignals().SetBestChain(chainActive.GetLocator()); - nLastSetChain = nNow; - } } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error while flushing: ") + e.what()); } @@ -2577,18 +2798,18 @@ void PruneAndFlush() { void static UpdateTip(CBlockIndex *pindexNew) { const CChainParams& chainParams = Params(); chainActive.SetTip(pindexNew); - + // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - + KOMODO_NEWBLOCKS++; LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, - 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(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - + 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(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), 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) @@ -2613,11 +2834,13 @@ void static UpdateTip(CBlockIndex *pindexNew) { } } -/** Disconnect chainActive's tip. */ -bool static DisconnectTip(CValidationState &state) { +/** + * Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and + * mempool.removeWithoutBranchId after this, with cs_main held. + */ +bool static DisconnectTip(CValidationState &state, bool fBare = false) { CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); - mempool.check(pcoinsTip); // Read block from disk. CBlock block; if (!ReadBlockFromDisk(block, pindexDelete)) @@ -2636,21 +2859,23 @@ bool static DisconnectTip(CValidationState &state) { // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) 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() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) - mempool.remove(tx, removed, true); + + if (!fBare) { + // 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() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + mempool.remove(tx, removed, true); + } + if (anchorBeforeDisconnect != anchorAfterDisconnect) { + // The anchor may not change between block disconnects, + // in which case we don't want to evict from the mempool yet! + mempool.removeWithAnchor(anchorBeforeDisconnect); + } } - if (anchorBeforeDisconnect != anchorAfterDisconnect) { - // The anchor may not change between block disconnects, - // in which case we don't want to evict from the mempool yet! - mempool.removeWithAnchor(anchorBeforeDisconnect); - } - mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); - mempool.check(pcoinsTip); + // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); // Get the current commitment tree @@ -2676,11 +2901,11 @@ 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. + * You probably want to call mempool.removeWithoutBranchId after this, with cs_main held. */ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { assert(pindexNew->pprev == chainActive.Tip()); - mempool.check(pcoinsTip); // Read block from disk. int64_t nTime1 = GetTimeMicros(); CBlock block; @@ -2720,7 +2945,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Remove conflicting transactions from the mempool. list txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); - mempool.check(pcoinsTip); + + // Remove transactions that expire at new block height from mempool + mempool.removeExpired(pindexNew->nHeight); + // Update chainActive & related variables. UpdateTip(pindexNew); // Tell wallet about transactions that went from mempool @@ -2735,7 +2963,9 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Update cached incremental witnesses //fprintf(stderr,"chaintip true\n"); GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true); - + + EnforceNodeDeprecation(pindexNew->nHeight); + 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); @@ -2749,7 +2979,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * static CBlockIndex* FindMostWorkChain() { do { CBlockIndex *pindexNew = NULL; - + // Find the best candidate header. { std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); @@ -2757,14 +2987,14 @@ static CBlockIndex* FindMostWorkChain() { 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 @@ -2821,77 +3051,113 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); - + + // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. + // - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain, + // then pindexFork will be null, and we would need to remove the entire chain including + // our genesis block. In practice this (probably) won't happen because of checks elsewhere. + auto reorgLength = pindexOldTip ? pindexOldTip->nHeight - (pindexFork ? pindexFork->nHeight : -1) : 0; + static_assert(MAX_REORG_LENGTH > 0, "We must be able to reorg some distance"); + if (reorgLength > MAX_REORG_LENGTH) { + auto msg = strprintf(_( + "A block chain reorganization has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + + _("Reorganization details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d, work %s"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight, pindexOldTip->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("New tip: %s, height %d, work %s"), + pindexMostWork->phashBlock->GetHex(), pindexMostWork->nHeight, pindexMostWork->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("Fork point: %s, height %d"), + pindexFork->phashBlock->GetHex(), pindexFork->nHeight) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + // Disconnect active blocks which are no longer in the best chain. + bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { if (!DisconnectTip(state)) return false; + fBlocksDisconnected = true; } if ( KOMODO_REWIND != 0 ) { - fprintf(stderr,"rewind start ht.%d\n",chainActive.Tip()->nHeight); - while ( KOMODO_REWIND > 0 && chainActive.Tip()->nHeight > KOMODO_REWIND ) + CBlockIndex *tipindex; + fprintf(stderr,">>>>>>>>>>> rewind start ht.%d -> KOMODO_REWIND.%d\n",chainActive.Tip()->nHeight,KOMODO_REWIND); + while ( KOMODO_REWIND > 0 && (tipindex= chainActive.Tip()) != 0 && tipindex->nHeight > KOMODO_REWIND ) { + fBlocksDisconnected = true; + fprintf(stderr,"%d ",(int32_t)tipindex->nHeight); + InvalidateBlock(state,tipindex); if ( !DisconnectTip(state) ) - { - InvalidateBlock(state,chainActive.Tip()); break; - } } - fprintf(stderr,"reached rewind.%d, best to do: ./komodo-cli stop\n",KOMODO_REWIND); - sleep(60); + fprintf(stderr,"reached rewind.%d, best to do: ./komodo-cli -ac_name=%s stop\n",KOMODO_REWIND,ASSETCHAINS_SYMBOL); + sleep(20); + fprintf(stderr,"resuming normal operations\n"); KOMODO_REWIND = 0; - return(true); + //return(true); } // 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)) { - if (state.IsInvalid()) { - // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); - state = CValidationState(); - fInvalidFound = true; - fContinue = false; - break; + // 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)) { + 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 { - // 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; + 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; + } } } } + + if (fBlocksDisconnected) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); } - + mempool.removeWithoutBranchId( + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + mempool.check(pcoinsTip); + // Callbacks/notifications for a new best chain. if (fInvalidFound) CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); else CheckForkWarningConditions(); - + return true; } @@ -2906,23 +3172,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { const CChainParams& chainParams = Params(); do { boost::this_thread::interruption_point(); - + bool fInitialDownload; { LOCK(cs_main); 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)) return false; pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(); } // 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(); @@ -2935,8 +3201,8 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (nLocalServices & NODE_NETWORK) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip); @@ -2944,23 +3210,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { } //else fprintf(stderr,"initial download skips propagation\n"); } 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; @@ -2969,11 +3235,14 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state)) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + mempool.removeWithoutBranchId( + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); return false; } } //LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); @@ -2983,16 +3252,19 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + InvalidChainFound(pindex); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + mempool.removeWithoutBranchId( + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); 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()) { @@ -3009,7 +3281,7 @@ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + // Remove the invalidity flag from all ancestors too. while (pindex != NULL) { if (pindex->nStatus & BLOCK_FAILED_MASK) { @@ -3028,7 +3300,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) 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); @@ -3049,9 +3321,9 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) pindexBestHeader = pindexNew; - + setDirtyBlockIndex.insert(pindexNew); - + return pindexNew; } @@ -3060,23 +3332,41 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl { pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; + CAmount sproutValue = 0; + for (auto tx : block.vtx) { + for (auto js : tx.vjoinsplit) { + sproutValue += js.vpub_old; + sproutValue -= js.vpub_new; + } + } + pindexNew->nSproutValue = sproutValue; + pindexNew->nChainSproutValue = boost::none; 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; + if (pindex->pprev) { + if (pindex->pprev->nChainSproutValue && pindex->nSproutValue) { + pindex->nChainSproutValue = *pindex->pprev->nChainSproutValue + *pindex->nSproutValue; + } else { + pindex->nChainSproutValue = boost::none; + } + } else { + pindex->nChainSproutValue = pindex->nSproutValue; + } { LOCK(cs_nBlockSequenceId); pindex->nSequenceId = nBlockSequenceId++; @@ -3097,19 +3387,19 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); } } - + 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) { nFile++; @@ -3120,7 +3410,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd pos.nFile = nFile; pos.nPos = vinfoBlockFile[nFile].nSize; } - + if (nFile != nLastBlockFile) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); @@ -3128,13 +3418,13 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd FlushBlockFile(!fKnown); 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; @@ -3153,7 +3443,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd return state.Error("out of disk space"); } } - + setDirtyFileInfo.insert(nFile); return true; } @@ -3161,14 +3451,14 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd 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) { @@ -3185,7 +3475,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne else return state.Error("out of disk space"); } - + return true; } @@ -3209,37 +3499,80 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl } if (blockhdr.GetBlockTime() > GetAdjustedTime() + 60) return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),REJECT_INVALID, "time-too-new"); + else if ( ASSETCHAINS_STAKED != 0 && pindex != 0 && pindex->pprev != 0 && pindex->nTime <= pindex->pprev->nTime ) + { + fprintf(stderr,"ht.%d %u vs ht.%d %u, is not monotonic\n",pindex->nHeight,pindex->nTime,pindex->pprev->nHeight,pindex->pprev->nTime); + return state.Invalid(error("CheckBlockHeader(): block timestamp needs to always increase"),REJECT_INVALID, "time-too-new"); + } // Check block version //if (block.nVersion < MIN_BLOCK_VERSION) // return state.DoS(100, error("CheckBlockHeader(): block version too low"),REJECT_INVALID, "version-too-low"); - + // Check Equihash solution is valid if ( fCheckPOW && !CheckEquihashSolution(&blockhdr, Params()) ) return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution"); // Check proof of work matches claimed amount /*komodo_index2pubkey33(pubkey33,pindex,height); - if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus()) ) - return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash");*/ + if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus()) ) + return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash");*/ return true; } -int32_t komodo_check_deposit(int32_t height,const CBlock& block); +int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime); + +int32_t komodo_reverify_blockcheck(CValidationState& state,int32_t height,CBlockIndex *pindex) +{ + static int32_t oneshot; + CBlockIndex *tipindex; int32_t rewindtarget; + if ( KOMODO_REWIND != 0 ) + oneshot = KOMODO_REWIND; + if ( oneshot == 0 && IsInitialBlockDownload() == 0 && (tipindex= chainActive.Tip()) != 0 ) + { + // if 200 blocks behind longestchain and no blocks for 2 hours + if ( KOMODO_LONGESTCHAIN > height+200 && KOMODO_NEWBLOCKS == 0 ) + { + if ( GetAdjustedTime() > tipindex->nTime+3600*2 ) + { + fprintf(stderr,"tip.%d longest.%d newblock.%d lag.%d blocktime.%u\n",tipindex->nHeight,KOMODO_LONGESTCHAIN,height,(int32_t)(GetAdjustedTime() - tipindex->nTime),tipindex->nTime); + KOMODO_REWIND = tipindex->nHeight - 11; + /* + rewindtarget = tipindex->nHeight - 11; + fprintf(stderr,"rewindtarget <- %d\n",rewindtarget); + oneshot = 1; + while ( rewindtarget > 0 && (tipindex= chainActive.Tip()) != 0 && tipindex->nHeight > rewindtarget ) + { + fprintf(stderr,"%d ",(int32_t)tipindex->nHeight); + InvalidateBlock(state,tipindex); + if ( !DisconnectTip(state) ) + break; + } + tipindex = chainActive.Tip(); + fprintf(stderr,"rewind done to %d\n",tipindex!=0?tipindex->nHeight:-1);*/ + } + } + } + return(0); +} + bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { uint8_t pubkey33[33]; - // These are checks that are independent of context. - + // 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(height,pindex,block,state,fCheckPOW)) return false; + //komodo_index2pubkey33(pubkey33,pindex,height); komodo_block2pubkey33(pubkey33,(CBlock *)&block); if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()) ) - return state.DoS(50, error("CheckBlock(): proof of work failed"),REJECT_INVALID, "high-hash"); - + { + komodo_reverify_blockcheck(state,height,pindex); + return state.DoS(33, error("CheckBlock(): proof of work failed"),REJECT_INVALID, "high-hash"); + } // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; @@ -3247,7 +3580,7 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat 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. @@ -3255,16 +3588,16 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat 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 if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) 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"), @@ -3273,12 +3606,12 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock(): more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); - + // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) { - if ( komodo_validate_interest(tx,komodo_block2height((CBlock *)&block),block.nTime,1) < 0 ) - return error("CheckBlock: komodo_validate_interest failed"); + if ( komodo_validate_interest(tx,height == 0 ? komodo_block2height((CBlock *)&block) : height,block.nTime,1) < 0 ) + return error("CheckBlock: komodo_validate_interest failed"); if (!CheckTransaction(tx, state, verifier)) return error("CheckBlock(): CheckTransaction failed"); } @@ -3290,10 +3623,11 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); - if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block) < 0 ) + if ( komodo_check_deposit(height,block,(pindex==0||pindex->pprev==0)?0:pindex->pprev->nTime) < 0 ) + //if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block,pindex==0||pindex->pprev==0?0:pindex->pprev->nTime) < 0 ) { static uint32_t counter; - if ( counter++ < 100 ) + if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) fprintf(stderr,"check deposit rejection\n"); return(false); } @@ -3307,11 +3641,11 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta uint256 hash = block.GetHash(); if (hash == consensusParams.hashGenesisBlock) return true; - + assert(pindexPrev); - + int nHeight = pindexPrev->nHeight+1; - + // Check proof of work if ( (nHeight < 235300 || nHeight > 236000) && block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) { @@ -3319,14 +3653,15 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); } - + // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - + if (fCheckpointsEnabled) { + // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) { CBlockIndex *heightblock = chainActive[nHeight]; @@ -3355,7 +3690,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta if (block.nVersion < 4) return state.Invalid(error("%s : rejected nVersion<4 block", __func__), REJECT_OBSOLETE, "bad-version"); - + return true; } @@ -3363,23 +3698,28 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; const Consensus::Params& consensusParams = Params().GetConsensus(); - + // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) { + + // Check transaction contextually against consensus rules at block height + if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { + return false; // Failure reason has been set in validation state object + } + int nLockTimeFlags = 0; int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) - ? pindexPrev->GetMedianTimePast() - : block.GetBlockTime(); + ? pindexPrev->GetMedianTimePast() + : block.GetBlockTime(); if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } } - - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - // Since MIN_BLOCK_VERSION = 4 all blocks with nHeight > 0 should satisfy this. - // This rule is not applied to the genesis block, which didn't include the height - // in the coinbase. + + // Enforce BIP 34 rule that the coinbase starts with serialized block height. + // In Zcash this has been enforced since launch, except that the genesis + // block didn't include the height in the coinbase (see Zcash protocol spec + // section '6.8 Bitcoin Improvement Proposals'). if (nHeight > 0) { CScript expect = CScript() << nHeight; @@ -3388,7 +3728,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); } } - + return true; } @@ -3405,9 +3745,17 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc pindex = miSelf->second; if (ppindex) *ppindex = pindex; - if (pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); - if ( pindex != 0 && IsInitialBlockDownload() == 0 ) // jl777 + if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) + { + komodo_reverify_blockcheck(state,pindex->nHeight,pindex); + if ( KOMODO_LONGESTCHAIN != 0 && pindex->nHeight > KOMODO_LONGESTCHAIN-100 ) + return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + else + { + pindex->nStatus &= ~BLOCK_FAILED_MASK; + } + } + if ( pindex != 0 && IsInitialBlockDownload() == 0 ) // jl777 debug test { if (!CheckBlockHeader(pindex->nHeight,pindex, block, state)) { @@ -3440,18 +3788,20 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return false; } } - return true; + return true; } - + if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state)) return false; - + // Get prev block index CBlockIndex* pindexPrev = NULL; if (hash != chainparams.GetConsensus().hashGenesisBlock) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) + { return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); + } pindexPrev = (*mi).second; if (pindexPrev == 0 || (pindexPrev->nStatus & BLOCK_FAILED_MASK) ) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); @@ -3459,7 +3809,10 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (!ContextualCheckBlockHeader(block, state, pindexPrev)) return false; if (pindex == NULL) - pindex = AddToBlockIndex(block); + { + if ( (pindex= AddToBlockIndex(block)) == 0 ) + fprintf(stderr,"couldnt add to block index\n"); + } if (ppindex) *ppindex = pindex; return true; @@ -3469,7 +3822,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - + CBlockIndex *&pindex = *ppindex; if (!AcceptBlockHeader(block, state, &pindex)) return false; @@ -3489,7 +3842,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // regardless of whether pruning is enabled; it should generally be safe to // not process unrequested blocks. bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); - + // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. if (fAlreadyHave) return true; @@ -3498,7 +3851,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high } - + // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); if ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) { @@ -3508,9 +3861,9 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } return false; } - + int nHeight = pindex->nHeight; - + // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); @@ -3527,10 +3880,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } - + if (fCheckForPruning) FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files - + return true; } @@ -3562,12 +3915,15 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc LOCK(cs_main); bool fRequested = MarkBlockAsReceived(pblock->GetHash()); fRequested |= fForceProcessing; - if (!checked) { + if (!checked) + { if ( pfrom != 0 ) + { Misbehaving(pfrom->GetId(), 1); + } return error("%s: CheckBlock FAILED", __func__); } - + // Store to disk CBlockIndex *pindex = NULL; bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); @@ -3578,10 +3934,10 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc if (!ret) return error("%s: AcceptBlock FAILED", __func__); } - + if (!ActivateBestChain(state, pblock)) return error("%s: ActivateBestChain failed", __func__); - + return true; } @@ -3589,14 +3945,14 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex { AssertLockHeld(cs_main); assert(pindexPrev == chainActive.Tip()); - + CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; // JoinSplit proofs are verified in ConnectBlock auto verifier = libzcash::ProofVerifier::Disabled(); - + // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) { @@ -3619,7 +3975,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex return false; } assert(state.IsValid()); - + return true; } @@ -3649,7 +4005,7 @@ void PruneOneBlockFile(const int fileNumber) pindex->nDataPos = 0; pindex->nUndoPos = 0; setDirtyBlockIndex.insert(pindex); - + // Prune from mapBlocksUnlinked -- any block we prune would have // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for @@ -3664,7 +4020,7 @@ void PruneOneBlockFile(const int fileNumber) } } } - + vinfoBlockFile[fileNumber].SetNull(); setDirtyFileInfo.insert(fileNumber); } @@ -3690,7 +4046,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { return; } - + unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files @@ -3699,21 +4055,21 @@ void FindFilesToPrune(std::set& setFilesToPrune) uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; uint64_t nBytesToPrune; int count=0; - + if (nCurrentUsage + nBuffer >= nPruneTarget) { for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; - + if (vinfoBlockFile[fileNumber].nSize == 0) continue; - + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? break; - + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) continue; - + PruneOneBlockFile(fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); @@ -3721,26 +4077,28 @@ void FindFilesToPrune(std::set& setFilesToPrune) count++; } } - + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", - nPruneTarget/1024/1024, nCurrentUsage/1024/1024, - ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, - nLastBlockWeCanPrune, count); + nPruneTarget/1024/1024, nCurrentUsage/1024/1024, + ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, + nLastBlockWeCanPrune, count); } bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::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!")); - + return true; } + FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { + static int32_t didinit[1000]; long fsize,fpos; int32_t incr = 16*1024*1024; if (pos.IsNull()) return NULL; boost::filesystem::path path = GetBlockPosFilename(pos, prefix); @@ -3752,6 +4110,26 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) LogPrintf("Unable to open file %s\n", path.string()); return NULL; } + if ( pos.nFile < sizeof(didinit)/sizeof(*didinit) && didinit[pos.nFile] == 0 && strcmp(prefix,(char *)"blk") == 0 ) + { + fpos = ftell(file); + fseek(file,0,SEEK_END); + fsize = ftell(file); + if ( fsize > incr ) + { + char *ignore = (char *)malloc(incr); + if ( ignore != 0 ) + { + rewind(file); + while ( fread(ignore,1,incr,file) == incr ) + fprintf(stderr,"."); + free(ignore); + fprintf(stderr,"blk.%d loaded %ld bytes set fpos.%ld loading.%d\n",(int)pos.nFile,(long)ftell(file),(long)fpos,KOMODO_LOADINGBLOCKS); + } + } + fseek(file,fpos,SEEK_SET); + didinit[pos.nFile] = 1; + } if (pos.nPos) { if (fseek(file, pos.nPos, SEEK_SET)) { LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); @@ -3779,30 +4157,32 @@ CBlockIndex * InsertBlockIndex(uint256 hash) { if (hash.IsNull()) return NULL; - + // 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; pindexNew->phashBlock = &((*mi).first); - + return pindexNew; } +//void komodo_pindex_init(CBlockIndex *pindex,int32_t height); + bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); if (!pblocktree->LoadBlockIndexGuts()) return false; - + boost::this_thread::interruption_point(); - + // Calculate nChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); @@ -3810,6 +4190,7 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) @@ -3822,14 +4203,34 @@ bool static LoadBlockIndexDB() if (pindex->pprev) { if (pindex->pprev->nChainTx) { pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; + if (pindex->pprev->nChainSproutValue && pindex->nSproutValue) { + pindex->nChainSproutValue = *pindex->pprev->nChainSproutValue + *pindex->nSproutValue; + } else { + pindex->nChainSproutValue = boost::none; + } } else { pindex->nChainTx = 0; + pindex->nChainSproutValue = boost::none; mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); } } else { pindex->nChainTx = pindex->nTx; + pindex->nChainSproutValue = pindex->nSproutValue; } } + // Construct in-memory chain of branch IDs. + // Relies on invariant: a block that does not activate a network upgrade + // will always be valid under the same consensus rules as its parent. + // Genesis block has a branch ID of zero by definition, but has no + // validity status because it is side-loaded into a fresh chain. + // Activation blocks will have branch IDs set (read from disk). + if (pindex->pprev) { + if (pindex->IsValid(BLOCK_VALID_CONSENSUS) && !pindex->nCachedBranchId) { + pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; + } + } else { + pindex->nCachedBranchId = SPROUT_BRANCH_ID; + } 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)) @@ -3838,8 +4239,9 @@ bool static LoadBlockIndexDB() pindex->BuildSkip(); if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) pindexBestHeader = pindex; + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } - + // Load block file info pblocktree->ReadLastBlockFile(nLastBlockFile); vinfoBlockFile.resize(nLastBlockFile + 1); @@ -3856,7 +4258,7 @@ bool static LoadBlockIndexDB() break; } } - + // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); set setBlkDataFiles; @@ -3866,6 +4268,7 @@ bool static LoadBlockIndexDB() if (pindex->nStatus & BLOCK_HAVE_DATA) { setBlkDataFiles.insert(pindex->nFile); } + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) { @@ -3874,21 +4277,21 @@ bool static LoadBlockIndexDB() return false; } } - + // Check whether we have ever pruned block & undo files pblocktree->ReadFlag("prunedblockfiles", fHavePruned); if (fHavePruned) LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\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("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); - + // Fill in-memory data BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { @@ -3901,8 +4304,9 @@ bool static LoadBlockIndexDB() if (pindex->pprev) { pindex->pprev->hashAnchorEnd = pindex->hashAnchor; } + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } - + // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) @@ -3910,14 +4314,16 @@ bool static LoadBlockIndexDB() chainActive.SetTip(it->second); // Set hashAnchorEnd for the end of best chain it->second->hashAnchorEnd = pcoinsTip->GetBestAnchor(); - + PruneBlockIndexCandidates(); - + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); - + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); + + EnforceNodeDeprecation(chainActive.Height(), true); + return true; } @@ -3936,7 +4342,7 @@ 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 @@ -3990,7 +4396,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } 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; @@ -4005,9 +4411,144 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth 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; +} +bool RewindBlockIndex(const CChainParams& params) +{ + LOCK(cs_main); + + // RewindBlockIndex is called after LoadBlockIndex, so at this point every block + // index will have nCachedBranchId set based on the values previously persisted + // to disk. By definition, a set nCachedBranchId means that the block was + // fully-validated under the corresponding consensus rules. Thus we can quickly + // identify whether the current active chain matches our expected sequence of + // consensus rule changes, with two checks: + // + // - BLOCK_ACTIVATES_UPGRADE is set only on blocks that activate upgrades. + // - nCachedBranchId for each block matches what we expect. + auto sufficientlyValidated = [¶ms](const CBlockIndex* pindex) { + auto consensus = params.GetConsensus(); + bool fFlagSet = pindex->nStatus & BLOCK_ACTIVATES_UPGRADE; + bool fFlagExpected = IsActivationHeightForAnyUpgrade(pindex->nHeight, consensus); + return fFlagSet == fFlagExpected && + pindex->nCachedBranchId && + *pindex->nCachedBranchId == CurrentEpochBranchId(pindex->nHeight, consensus); + }; + + int nHeight = 1; + while (nHeight <= chainActive.Height()) { + if (!sufficientlyValidated(chainActive[nHeight])) { + break; + } + nHeight++; + } + + // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 + auto rewindLength = chainActive.Height() - nHeight; + if (rewindLength > 0 && rewindLength > MAX_REORG_LENGTH) { + auto pindexOldTip = chainActive.Tip(); + auto pindexRewind = chainActive[nHeight - 1]; + auto msg = strprintf(_( + "A block chain rewind has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), rewindLength, MAX_REORG_LENGTH) + "\n\n" + + _("Rewind details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight) + "\n" + + "- " + strprintf(_("Rewinding to: %s, height %d"), + pindexRewind->phashBlock->GetHex(), pindexRewind->nHeight) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + + CValidationState state; + CBlockIndex* pindex = chainActive.Tip(); + while (chainActive.Height() >= nHeight) { + if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, don't try rewinding past the HAVE_DATA point; + // since older blocks can't be served anyway, there's + // no need to walk further, and trying to DisconnectTip() + // will fail (and require a needless reindex/redownload + // of the blockchain). + break; + } + if (!DisconnectTip(state, true)) { + return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + } + // Occasionally flush state to disk. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) + return false; + } + + // Reduce validity flag and have-data flags. + // We do this after actual disconnecting, otherwise we'll end up writing the lack of data + // to disk before writing the chainstate, resulting in a failure to continue if interrupted. + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + CBlockIndex* pindexIter = it->second; + + // Note: If we encounter an insufficiently validated block that + // is on chainActive, it must be because we are a pruning node, and + // this block or some successor doesn't HAVE_DATA, so we were unable to + // rewind all the way. Blocks remaining on chainActive at this point + // must not have their validity reduced. + if (!sufficientlyValidated(pindexIter) && !chainActive.Contains(pindexIter)) { + // Reduce validity + pindexIter->nStatus = + std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | + (pindexIter->nStatus & ~BLOCK_VALID_MASK); + // Remove have-data flags + pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO); + // Remove branch ID + pindexIter->nStatus &= ~BLOCK_ACTIVATES_UPGRADE; + pindexIter->nCachedBranchId = boost::none; + // Remove storage location + pindexIter->nFile = 0; + pindexIter->nDataPos = 0; + pindexIter->nUndoPos = 0; + // Remove various other things + pindexIter->nTx = 0; + pindexIter->nChainTx = 0; + pindexIter->nSproutValue = boost::none; + pindexIter->nChainSproutValue = boost::none; + pindexIter->nSequenceId = 0; + // Make sure it gets written + setDirtyBlockIndex.insert(pindexIter); + if (pindexIter == pindexBestInvalid) + { + fprintf(stderr,"Reset invalid block marker if it was pointing to this block\n"); + pindexBestInvalid = NULL; + } + + // Update indices + setBlockIndexCandidates.erase(pindexIter); + auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); + while (ret.first != ret.second) { + if (ret.first->second == pindexIter) { + mapBlocksUnlinked.erase(ret.first++); + } else { + ++ret.first; + } + } + } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) { + setBlockIndexCandidates.insert(pindexIter); + } + } + + PruneBlockIndexCandidates(); + + CheckBlockIndex(); + + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { + return false; + } + return true; } @@ -4034,7 +4575,7 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); - + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; } @@ -4051,7 +4592,6 @@ bool LoadBlockIndex() KOMODO_LOADINGBLOCKS = 0; return false; } - //KOMODO_LOADINGBLOCKS = 0; fprintf(stderr,"finished loading blocks %s\n",ASSETCHAINS_SYMBOL); return true; } @@ -4060,19 +4600,19 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); - + // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); - + // 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 { @@ -4086,6 +4626,8 @@ bool InitBlockIndex() { if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) return error("LoadBlockIndex(): writing genesis block to disk failed"); CBlockIndex *pindex = AddToBlockIndex(block); + if ( pindex == 0 ) + return error("LoadBlockIndex(): couldnt add to block index"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("LoadBlockIndex(): genesis block not accepted"); if (!ActivateBestChain(state, &block)) @@ -4096,7 +4638,7 @@ bool InitBlockIndex() { return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } } - + return true; } @@ -4108,7 +4650,7 @@ 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 @@ -4116,7 +4658,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) 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 @@ -4147,17 +4689,17 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) CBlock block; blkdat >> block; nRewind = blkdat.GetPos(); - + // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); if (hash != chainparams.GetConsensus().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()); + 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; @@ -4168,7 +4710,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } else if (hash != chainparams.GetConsensus().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); @@ -4181,7 +4723,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) if (ReadBlockFromDisk(mapBlockIndex[hash]!=0?mapBlockIndex[hash]->nHeight:0,block, it->second)) { LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), - head.ToString()); + head.ToString()); CValidationState dummy; if (ProcessNewBlock(0,dummy, NULL, &block, true, &it->second)) { @@ -4211,9 +4753,9 @@ 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.) @@ -4221,20 +4763,20 @@ void static CheckBlockIndex() 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. @@ -4256,7 +4798,7 @@ void static CheckBlockIndex() if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = 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. @@ -4342,7 +4884,7 @@ void static CheckBlockIndex() } // 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) { @@ -4385,7 +4927,7 @@ void static CheckBlockIndex() } } } - + // Check that we actually traversed the entire map. assert(nNodes == forward.size()); } @@ -4400,20 +4942,20 @@ std::string GetWarnings(const std::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 mining 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; @@ -4424,7 +4966,7 @@ std::string GetWarnings(const std::string& strFor) 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); @@ -4441,7 +4983,7 @@ std::string GetWarnings(const std::string& strFor) } } } - + if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") @@ -4467,7 +5009,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { switch (inv.type) { - case MSG_TX: + case MSG_TX: { assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) @@ -4479,14 +5021,14 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); recentRejects->reset(); } - + return recentRejects->contains(inv.hash) || - mempool.exists(inv.hash) || - mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); + mempool.exists(inv.hash) || + mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); } - case MSG_BLOCK: - return mapBlockIndex.count(inv.hash); + case MSG_BLOCK: + return mapBlockIndex.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -4495,21 +5037,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) 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; @@ -4524,8 +5066,8 @@ void static ProcessGetData(CNode* pfrom) // chain if they are valid, and no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about. send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); + (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -4613,17 +5155,17 @@ void static ProcessGetData(CNode* pfrom) vNotFound.push_back(inv); } } - + // Track requests for our stuff. GetMainSignals().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 @@ -4646,10 +5188,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } - - - - + + + + if (strCommand == "version") { // Each connection can only send one version message @@ -4659,7 +5201,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - + int64_t nTime; CAddress addrMe; CAddress addrFrom; @@ -4674,7 +5216,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return false; } - + + // When Overwinter is active, reject incoming connections from non-Overwinter nodes + const Consensus::Params& params = Params().GetConsensus(); + if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER) + && pfrom->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion) + { + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + if (pfrom->nVersion == 10300) pfrom->nVersion = 300; if (!vRecv.empty()) @@ -4689,7 +5244,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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) { @@ -4697,26 +5252,26 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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 @@ -4733,7 +5288,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushAddress(addr); } } - + // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { @@ -4748,56 +5303,72 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, addrman.Good(addrFrom); } } - + // Relay alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); + 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); - + int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); } - - + + 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; } } - - + + + // Disconnect existing peer connection when: + // 1. The version message has been received + // 2. Overwinter is active + // 3. Peer version is pre-Overwinter + else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER) + && (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)) + { + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + + 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; @@ -4806,7 +5377,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } - + // Store the new addresses vector vAddrOk; int64_t nNow = GetAdjustedTime(); @@ -4814,7 +5385,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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); @@ -4858,8 +5429,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fOneShot) pfrom->fDisconnect = true; } - - + + else if (strCommand == "inv") { vector vInv; @@ -4869,24 +5440,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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)) { @@ -4910,21 +5481,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } } - + // Track requests for our stuff GetMainSignals().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; @@ -4934,29 +5505,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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") { 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); @@ -4980,19 +5551,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + else if (strCommand == "getheaders") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + if (IsInitialBlockDownload()) return true; - + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -5009,7 +5580,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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; @@ -5032,37 +5603,37 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, fprintf(stderr,"you can ignore redundant getheaders from peer.%d %d prev.%d\n",(int32_t)pfrom->id,(int32_t)(pindex ? pindex->nHeight : -1),pfrom->lasthdrsreq); } } - - + + else if (strCommand == "tx") { vector vWorkQueue; vector vEraseQueue; CTransaction tx; vRecv >> tx; - + CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); - + LOCK(cs_main); - + bool fMissingInputs = false; CValidationState state; - + pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); - + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { 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()); - + 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++) @@ -5082,8 +5653,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // 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)) @@ -5113,15 +5684,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); } } - + BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); + EraseOrphanTx(hash); } // TODO: currently, prohibit joinsplits from entering mapOrphans else if (fMissingInputs && tx.vjoinsplit.size() == 0) { 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); @@ -5130,7 +5701,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { assert(recentRejects); recentRejects->insert(tx.GetHash()); - + if (pfrom->fWhitelisted) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due @@ -5146,7 +5717,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s (code %d))\n", - tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); + tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); } } } @@ -5154,20 +5725,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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->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" && !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) { @@ -5179,14 +5750,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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; @@ -5196,17 +5767,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!AcceptBlockHeader(header, state, &pindexLast)) { int nDoS; - if (state.IsInvalid(nDoS)) { + if (state.IsInvalid(nDoS)) + { if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS/nDoS); return error("invalid header received"); } } } - + 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 @@ -5218,20 +5790,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } } - + CheckBlockIndex(); } - + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; - + CInv inv(MSG_BLOCK, block.GetHash()); LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - + pfrom->AddInventoryKnown(inv); - + CValidationState state; // Process all blocks from whitelisted peers, even if not requested, // unless we're still syncing with the network. @@ -5248,10 +5820,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), nDoS); } } - + } - - + + // 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. @@ -5266,18 +5838,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } pfrom->fSentAddr = true; - + pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); + pfrom->PushAddress(addr); } - - + + else if (strCommand == "mempool") { LOCK2(cs_main, pfrom->cs_filter); - + std::vector vtxid; mempool.queryHashes(vtxid); vector vInv; @@ -5287,7 +5859,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fInMemPool = mempool.lookup(hash, tx); if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || - (!pfrom->pfilter)) + (!pfrom->pfilter)) vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage("inv", vInv); @@ -5297,8 +5869,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } - - + + else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) @@ -5319,8 +5891,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("pong", nonce); } } - - + + else if (strCommand == "pong") { int64_t pingUsecEnd = nTimeReceived; @@ -5328,10 +5900,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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) { @@ -5363,27 +5935,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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); + 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) { @@ -5394,7 +5966,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); + alert.RelayTo(pnode); } } else { @@ -5408,13 +5980,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + 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); @@ -5427,13 +5999,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } 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) @@ -5447,8 +6019,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 100); } } - - + + else if (strCommand == "filterclear") { LOCK(pfrom->cs_filter); @@ -5456,18 +6028,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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; @@ -5485,14 +6057,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. } - + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } - - - + + + return true; } @@ -5501,7 +6073,7 @@ bool ProcessMessages(CNode* pfrom) { //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); - + // // Message format // (4) message start @@ -5511,41 +6083,41 @@ bool ProcessMessages(CNode* pfrom) // (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("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // 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(Params().MessageStart())) @@ -5554,10 +6126,10 @@ bool ProcessMessages(CNode* pfrom) 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); @@ -5565,10 +6137,10 @@ bool ProcessMessages(CNode* pfrom) if (nChecksum != hdr.nChecksum) { LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, - SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); + SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } - + // Process message bool fRet = false; try @@ -5602,17 +6174,17 @@ bool ProcessMessages(CNode* pfrom) } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); } - + if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, 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; } @@ -5624,7 +6196,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Don't send anything until we get its version message if (pto->nVersion == 0) return true; - + // // Message: ping // @@ -5653,11 +6225,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) 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)) @@ -5668,14 +6240,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) pnode->addrKnown.reset(); - + // Rebroadcast our address AdvertizeLocal(pnode); } if (!vNodes.empty()) nLastRebroadcast = GetTime(); } - + // // Message: addr // @@ -5701,7 +6273,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!vAddr.empty()) pto->PushMessage("addr", vAddr); } - + CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { if (pto->fWhitelisted) @@ -5717,11 +6289,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } state.fShouldBan = false; } - + BOOST_FOREACH(const CBlockReject& reject, state.rejects) - pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); + pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); - + // Start block sync if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); @@ -5736,7 +6308,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); } } - + // 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. @@ -5744,7 +6316,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { GetMainSignals().Broadcast(nTimeBestReceived); } - + // // Message: inventory // @@ -5758,7 +6330,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { if (pto->setInventoryKnown.count(inv)) continue; - + // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { @@ -5769,14 +6341,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((UintToArith256(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) { @@ -5792,7 +6364,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } 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) { @@ -5824,7 +6396,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->fDisconnect = true; } } - + // // Message: getdata (blocks) // @@ -5837,7 +6409,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); + pindex->nHeight, pto->id); } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { @@ -5846,7 +6418,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } } - + // // Message: getdata (non-blocks) // @@ -5871,14 +6443,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); - + } 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)); - } +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)); +} @@ -5892,7 +6464,7 @@ public: for (; it1 != mapBlockIndex.end(); it1++) delete (*it1).second; mapBlockIndex.clear(); - + // orphan transactions mapOrphanTransactions.clear(); mapOrphanTransactionsByPrev.clear(); @@ -5901,6 +6473,24 @@ public: extern "C" const char* getDataDir() { - return GetDataDir().string().c_str(); + return GetDataDir().string().c_str(); } + +// Set default values of new CMutableTransaction based on consensus rules at given height. +CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight) +{ + CMutableTransaction mtx; + + bool isOverwintered = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_OVERWINTER); + if (isOverwintered) { + mtx.fOverwintered = true; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nVersion = 3; + // Expiry height is not set. Only fields required for a parser to treat as a valid Overwinter V3 tx. + + // TODO: In future, when moving from Overwinter to Sapling, it will be useful + // to set the expiry height to: min(activation_height - 1, default_expiry_height) + } + return mtx; +}