diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index e098cfdae..284cff134 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -22,6 +22,7 @@ public: MOCK_CONST_METHOD0(GetRejectReason, std::string()); }; +int32_t futureblock; TEST(CheckBlock, VersionTooLow) { auto verifier = libzcash::ProofVerifier::Strict(); @@ -30,7 +31,7 @@ TEST(CheckBlock, VersionTooLow) { MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1); - EXPECT_FALSE(CheckBlock(0,0,block, state, verifier, false, false)); + EXPECT_FALSE(CheckBlock(&futureblock,0,0,block, state, verifier, false, false)); } @@ -230,4 +231,4 @@ TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSproutTx) { // Revert to default UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); -} \ No newline at end of file +} diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index b9680f9fd..72136427c 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1035,7 +1035,7 @@ int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_ cmptime -= 16000; if ( (int64_t)tx.nLockTime < cmptime-3600 ) { - if ( tx.nLockTime != 1477258935 || dispflag != 0 ) + if ( tx.nLockTime != 1477258935 && dispflag != 0 ) { fprintf(stderr,"komodo_validate_interest.%d reject.%d [%d] locktime %u cmp2.%u\n",dispflag,txheight,(int32_t)(tx.nLockTime - (cmptime-3600)),(uint32_t)tx.nLockTime,cmptime); } diff --git a/src/main.cpp b/src/main.cpp index 87e14bbaa..c69cb7e6d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2583,9 +2583,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); - + int32_t futureblock; // 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, fCheckPOW, !fJustCheck)) + if (!CheckBlock(&futureblock,pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck)) { fprintf(stderr,"checkblock failure in connectblock\n"); return false; @@ -3740,7 +3740,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& blockhdr, CValidationState& state, bool fCheckPOW) +bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, const CBlockHeader& blockhdr, CValidationState& state, bool fCheckPOW) { // Check timestamp if ( 0 ) @@ -3758,11 +3758,12 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl fprintf(stderr," <- chainTip\n"); } } + *futureblockp = 0; if (blockhdr.GetBlockTime() > GetAdjustedTime() + 60) { CBlockIndex *tipindex; - //fprintf(stderr,"ht.%d future block %u vs time.%u + 60\n",height,(uint32_t)blockhdr.GetBlockTime(),(uint32_t)GetAdjustedTime()); - if ( (tipindex= chainActive.Tip()) != 0 && tipindex->GetBlockHash() == blockhdr.hashPrevBlock && blockhdr.GetBlockTime() < GetAdjustedTime() + 60*2 ) + fprintf(stderr,"ht.%d future block %u vs time.%u + 60\n",height,(uint32_t)blockhdr.GetBlockTime(),(uint32_t)GetAdjustedTime()); + if ( (tipindex= chainActive.Tip()) != 0 && tipindex->GetBlockHash() == blockhdr.hashPrevBlock && blockhdr.GetBlockTime() < GetAdjustedTime() + 60 + 5 ) { //fprintf(stderr,"it is the next block, let's wait for %d seconds\n",GetAdjustedTime() + 60 - blockhdr.GetBlockTime()); while ( blockhdr.GetBlockTime() > GetAdjustedTime() + 60 ) @@ -3771,6 +3772,8 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl } else { + if (blockhdr.GetBlockTime() < GetAdjustedTime() + 600) + *futureblockp = 1; return false; //state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),REJECT_INVALID, "time-too-new"); } } @@ -3799,7 +3802,7 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime); int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height); -bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, +bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { @@ -3809,7 +3812,7 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(height,pindex,block,state,fCheckPOW)) + if (!CheckBlockHeader(futureblockp,height,pindex,block,state,fCheckPOW)) { //fprintf(stderr,"checkblockheader error PoW.%d\n",fCheckPOW); return false; @@ -3991,11 +3994,12 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn //static uint256 komodo_requestedhash; //static int32_t komodo_requestedcount; -bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) +bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) { static uint256 zero; const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); + // Check for duplicate uint256 hash = block.GetHash(); BlockMap::iterator miSelf = mapBlockIndex.find(hash); @@ -4018,7 +4022,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc // fprintf(stderr,"accepthdr %s already known but no pindex\n",hash.ToString().c_str()); return true; } - if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state,0)) + if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state,0)) { //fprintf(stderr,"AcceptBlockHeader: CheckBlockHeader failed\n"); return false; @@ -4030,7 +4034,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) { - fprintf(stderr,"AcceptBlockHeader hashPrevBlock %s not found\n",block.hashPrevBlock.ToString().c_str()); + //fprintf(stderr,"AcceptBlockHeader hashPrevBlock %s not found\n",block.hashPrevBlock.ToString().c_str()); /*if ( komodo_requestedhash == zero ) { komodo_requestedhash = block.hashPrevBlock; @@ -4042,8 +4046,8 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc pindexPrev = (*mi).second; if (pindexPrev == 0 ) { - fprintf(stderr,"AcceptBlockHeader failed no pindexPrev %s\n",block.hashPrevBlock.ToString().c_str()); - /*if ( komodo_requestedhash == zero ) + /*fprintf(stderr,"AcceptBlockHeader failed no pindexPrev %s\n",block.hashPrevBlock.ToString().c_str()); + if ( komodo_requestedhash == zero ) { komodo_requestedhash = block.hashPrevBlock; komodo_requestedcount = 0; @@ -4076,13 +4080,13 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) +bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); CBlockIndex *&pindex = *ppindex; - if (!AcceptBlockHeader(block, state, &pindex)) + if (!AcceptBlockHeader(futureblockp,block, state, &pindex)) { //fprintf(stderr,"AcceptBlockHeader rejected\n"); return false; @@ -4117,9 +4121,9 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - if ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) + if ((!CheckBlock(futureblockp,pindex->nHeight,pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) { - if (state.IsInvalid() && !state.CorruptionPossible()) { + if (*futureblockp == 0 && state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); } @@ -4192,13 +4196,13 @@ CBlockIndex *komodo_ensure(CBlock *pblock,uint256 hash) bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks - bool checked; uint256 hash; + bool checked; uint256 hash; int32_t futureblock=0; auto verifier = libzcash::ProofVerifier::Disabled(); hash = pblock->GetHash(); //fprintf(stderr,"process newblock %s\n",hash.ToString().c_str()); if ( chainActive.Tip() != 0 ) komodo_currentheight_set(chainActive.Tip()->nHeight); - checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); + checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); { LOCK(cs_main); bool fRequested = MarkBlockAsReceived(hash); @@ -4208,7 +4212,7 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo checked = 0; fprintf(stderr,"passed checkblock but failed checkPOW.%d\n",from_miner && ASSETCHAINS_STAKED == 0); } - if (!checked) + if (!checked && futureblock == 0) { if ( pfrom != 0 ) { @@ -4222,17 +4226,17 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo // without the komodo_ensure call, it is quite possible to get a non-error but null pindex returned from AcceptBlockHeader. In a 2 node network, it will be a long time before that block is reprocessed. Even though restarting makes it rescan, it seems much better to keep the nodes in sync komodo_ensure(pblock,hash); } - bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); + bool ret = AcceptBlock(&futureblock,*pblock, state, &pindex, fRequested, dbp); if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } CheckBlockIndex(); - if (!ret) + if (!ret && futureblock == 0) return error("%s: AcceptBlock FAILED", __func__); //else fprintf(stderr,"added block %s %p\n",pindex->GetBlockHash().ToString().c_str(),pindex->pprev); } - if (!ActivateBestChain(state, pblock)) + if (futureblock == 0 && !ActivateBestChain(state, pblock)) return error("%s: ActivateBestChain failed", __func__); return true; @@ -4255,7 +4259,8 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex //fprintf(stderr,"TestBlockValidity failure A checkPOW.%d\n",fCheckPOW); return false; } - if (!CheckBlock(indexDummy.nHeight,0,block, state, verifier, fCheckPOW, fCheckMerkleRoot)) + int32_t futureblock; + if (!CheckBlock(&futureblock,indexDummy.nHeight,0,block, state, verifier, fCheckPOW, fCheckMerkleRoot)) { //fprintf(stderr,"TestBlockValidity failure B checkPOW.%d\n",fCheckPOW); return false; @@ -4665,7 +4670,8 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth if (!ReadBlockFromDisk(block, pindex,0)) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state, verifier,0)) + int32_t futureblock; + if (nCheckLevel >= 1 && !CheckBlock(&futureblock,pindex->nHeight,pindex,block, state, verifier,0)) return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -6074,12 +6080,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("non-continuous headers sequence"); } + int32_t futureblock; //fprintf(stderr,"headers msg nCount.%d\n",(int32_t)nCount); - if (!AcceptBlockHeader(header, state, &pindexLast)) { + if (!AcceptBlockHeader(&futureblock,header, state, &pindexLast)) { int nDoS; if (state.IsInvalid(nDoS)) { - if (nDoS > 0) + if (nDoS > 0 && futureblock == 0) Misbehaving(pfrom->GetId(), nDoS/nDoS); return error("invalid header received"); } diff --git a/src/main.h b/src/main.h index f52fa222d..ef46cd83d 100644 --- a/src/main.h +++ b/src/main.h @@ -796,8 +796,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false,bool fCheckPOW = false); /** Context-independent validity checks */ -bool CheckBlockHeader(int32_t height,CBlockIndex *pindex,const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); -bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, +bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); +bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, bool fCheckPOW = true, bool fCheckMerkleRoot = true); @@ -815,8 +815,8 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * - The only caller of AcceptBlock verifies JoinSplit proofs elsewhere. * If dbp is non-NULL, the file is known to already reside on disk */ -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); -bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); +bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); +bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d14de08b9..d9eb9debc 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -401,15 +401,20 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list } } +int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag); + void CTxMemPool::removeExpired(unsigned int nBlockHeight) { + CBlockIndex *tipindex; // Remove expired txs from the mempool LOCK(cs); list transactionsToRemove; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { const CTransaction& tx = it->GetTx(); - if (IsExpiredTx(tx, nBlockHeight)) { + tipindex = chainActive.Tip(); + if (IsExpiredTx(tx, nBlockHeight) || (tipindex != 0 && komodo_validate_interest(tx,tipindex->nHeight+1,tipindex->GetMedianTimePast() + 777,1)) < 0) + { transactionsToRemove.push_back(tx); } }