diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bc26c013e..4ee8ea8b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [3.7.7.0] +### Fixed + - Beacon validation are now done when accepting blocks, not when receiving, + #899 (@denravonska). + - Fix crashes due to buffer overflow in encrypt/decrypt, #890 (@denravonska). + - Rewrite reorganize routine to be more reliable and drop contracts received + or issued while on a side chain to help reducing forks, #902 (@tomasbrod). + +## [3.7.6.0] +Internal test version used to sort out the forks. + ## [3.7.5.0] 2018-01-24 ### Fixed - Fix crash when switching to new tally on block 1144120, #868 (@denravonska). diff --git a/src/beacon.cpp b/src/beacon.cpp index a36c84aed2..4bdbb11bec 100755 --- a/src/beacon.cpp +++ b/src/beacon.cpp @@ -136,13 +136,11 @@ std::string RetrieveBeaconValueWithMaxAge(const std::string& cpid, int64_t iMaxS : value; } -bool VerifyBeaconContractTx(const std::string& txhashBoinc) +bool VerifyBeaconContractTx(const CTransaction& tx) { - // Mandatory condition handled in CheckBlock - // Check if tx contains beacon advertisement and evaluate for certain conditions - std::string chkMessageType = ExtractXML(txhashBoinc, "", ""); - std::string chkMessageAction = ExtractXML(txhashBoinc, "", ""); + std::string chkMessageType = ExtractXML(tx.hashBoinc, "", ""); + std::string chkMessageAction = ExtractXML(tx.hashBoinc, "", ""); if (chkMessageType != "beacon") return true; // Not beacon contract @@ -150,8 +148,8 @@ bool VerifyBeaconContractTx(const std::string& txhashBoinc) if (chkMessageAction != "A") return true; // Not an add contract for beacon - std::string chkMessageContract = ExtractXML(txhashBoinc, "", ""); - std::string chkMessageContractCPID = ExtractXML(txhashBoinc, "", ""); + std::string chkMessageContract = ExtractXML(tx.hashBoinc, "", ""); + std::string chkMessageContractCPID = ExtractXML(tx.hashBoinc, "", ""); // Here we GetBeaconElements for the contract in the tx std::string tx_out_cpid; std::string tx_out_address; @@ -174,7 +172,7 @@ bool VerifyBeaconContractTx(const std::string& txhashBoinc) } int64_t chkiAge = pindexBest != NULL - ? pindexBest->nTime - mvApplicationCacheTimestamp[chkKey] + ? tx.nLockTime - mvApplicationCacheTimestamp[chkKey] : 0; int64_t chkSecondsBase = 60 * 24 * 30 * 60; diff --git a/src/beacon.h b/src/beacon.h index c19a8d6cbc..4d7fba5274 100755 --- a/src/beacon.h +++ b/src/beacon.h @@ -4,6 +4,7 @@ #pragma once +#include "fwd.h" #include //! @@ -63,4 +64,4 @@ std::string GetBeaconPublicKey(const std::string& cpid, bool bAdvertisingBeacon) int64_t BeaconTimeStamp(const std::string& cpid, bool bZeroOutAfterPOR); bool HasActiveBeacon(const std::string& cpid); -bool VerifyBeaconContractTx(const std::string& txhashBoinc); +bool VerifyBeaconContractTx(const CTransaction& tx); diff --git a/src/clientversion.h b/src/clientversion.h index 2dd1eec3e8..0f8118ad4a 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,7 +8,7 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 3 #define CLIENT_VERSION_MINOR 7 -#define CLIENT_VERSION_REVISION 5 +#define CLIENT_VERSION_REVISION 7 #define CLIENT_VERSION_BUILD 0 // Converts the parameter X to a string after macro replacement on X has been performed. diff --git a/src/crypter.cpp b/src/crypter.cpp index a89bfb0373..5157289971 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -241,15 +241,22 @@ bool GridDecryptWithSalt(const std::vector& vchCiphertext,std::ve int nLen = vchCiphertext.size(); int nPLen = nLen, nFLen = 0; bool fOk = true; + + // Allocate data for the plaintext string. This is always equal to lower + // than the length of the encrypted string. Stray data is discarded + // after successfully decrypting. + vchPlaintext.resize(nLen); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if(!ctx) throw std::runtime_error("Error allocating cipher context"); - if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKeyGridcoin, chIVGridcoin); + if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKeyGridcoin, chIVGridcoin); if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen); if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0])+nPLen, &nFLen); EVP_CIPHER_CTX_free(ctx); if (!fOk) return false; + vchPlaintext.resize(nPLen + nFLen); return true; } @@ -348,12 +355,10 @@ std::string AdvancedDecryptWithHWID(std::string data) std::string AdvancedCryptWithSalt(std::string boinchash, std::string salt) { - try { std::vector vchSecret( boinchash.begin(), boinchash.end() ); - std::string d1 = " "; - std::vector vchCryptedSecret(d1.begin(),d1.end()); + std::vector vchCryptedSecret; GridEncryptWithSalt(vchSecret, vchCryptedSecret,salt); std::string encrypted = EncodeBase64(UnsignedVectorToString(vchCryptedSecret)); @@ -363,21 +368,14 @@ std::string AdvancedCryptWithSalt(std::string boinchash, std::string salt) printf("Error while encrypting %s",boinchash.c_str()); return ""; } - catch(...) - { - printf("Error while encrypting 2."); - return ""; - } - } std::string AdvancedDecryptWithSalt(std::string boinchash_encrypted, std::string salt) { try{ std::string pre_encrypted_boinchash = DecodeBase64(boinchash_encrypted); - std::string d2 = " "; std::vector vchCryptedSecret(pre_encrypted_boinchash.begin(),pre_encrypted_boinchash.end()); - std::vector vchPlaintext(d2.begin(),d2.end()); + std::vector vchPlaintext; GridDecryptWithSalt(vchCryptedSecret,vchPlaintext,salt); std::string decrypted = UnsignedVectorToString(vchPlaintext); return decrypted; @@ -386,11 +384,4 @@ std::string AdvancedDecryptWithSalt(std::string boinchash_encrypted, std::string printf("Error while decrypting %s",boinchash_encrypted.c_str()); return ""; } - catch(...) - { - printf("Error while decrypting 2."); - return ""; - } } - - diff --git a/src/fwd.h b/src/fwd.h index 0153e7b737..c59a70599a 100755 --- a/src/fwd.h +++ b/src/fwd.h @@ -6,6 +6,7 @@ class CBlock; class CBlockIndex; class CNetAddr; +class CTransaction; // Gridcoin struct MiningCPID; diff --git a/src/main.cpp b/src/main.cpp index b321dfea8c..10eb7717ea 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -70,6 +70,7 @@ std::string ExtractValue(std::string data, std::string delimiter, int pos); extern MiningCPID GetBoincBlockByIndex(CBlockIndex* pblockindex); json_spirit::Array MagnitudeReport(std::string cpid); extern void AddCPIDBlockHash(const std::string& cpid, const uint256& blockhash); +void RemoveCPIDBlockHash(const std::string& cpid, const uint256& blockhash); extern void ZeroOutResearcherTotals(std::string cpid); extern StructCPID GetLifetimeCPID(const std::string& cpid, const std::string& sFrom); extern std::string getCpuHash(); @@ -109,7 +110,6 @@ extern CBlockIndex* GetHistoricalMagnitude(std::string cpid); extern double GetOutstandingAmountOwed(StructCPID &mag, std::string cpid, int64_t locktime, double& total_owed, double block_magnitude); - extern double GetOwedAmount(std::string cpid); extern void DeleteCache(std::string section, std::string keyname); @@ -1357,7 +1357,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput return error("AcceptToMemoryPool : CheckTransaction failed"); // Verify beacon contract in tx if found - if (!VerifyBeaconContractTx(tx.hashBoinc)) + if (!VerifyBeaconContractTx(tx)) return tx.DoS(25, error("AcceptToMemoryPool : bad beacon contract in tx %s; rejected", tx.GetHash().ToString().c_str())); // Coinbase is only valid in a block, not as a loose transaction @@ -2584,10 +2584,25 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) { bDiscTxFailed = true; } + + /* Delete the contract. + * Previous version will be reloaded in reoranize. */ + { + std::string sMType = ExtractXML(vtx[i].hashBoinc, "", ""); + if(!sMType.empty()) + { + std::string sMKey = ExtractXML(vtx[i].hashBoinc, "", ""); + DeleteCache(sMKey, sMType); + if(fDebug) + printf("DisconnectBlock: Delete contract %s %s\n", sMType.c_str(), sMKey.c_str()); + } + } + } // Update block index on disk without changing it in memory. // The memory index structure will be changed after the db commits. + // Brod: I do not like this... if (pindex->pprev) { CDiskBlockIndex blockindexPrev(pindex->pprev); @@ -2600,8 +2615,7 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) for (auto const& tx : vtx) SyncWithWallets(tx, this, false, false); - // We normally fail to disconnect a block if we can't find the previous input due to "DisconnectInputs() : ReadTxIndex failed". Imo, I believe we should let this call succeed, otherwise a chain can never be re-organized in this circumstance. - if (bDiscTxFailed && fDebug3) printf("!DisconnectBlock()::Failed, recovering. "); + if (bDiscTxFailed) return error("DisconnectBlock(): Failed"); return true; } @@ -3349,282 +3363,346 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck, boo } - - -bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +bool ForceReorganizeToHash(uint256 NewHash) { - printf("REORGANIZE\n"); - // Find the fork - CBlockIndex* pfork = pindexBest; - CBlockIndex* plonger = pindexNew; - while (pfork != plonger) - { - while (plonger->nHeight > pfork->nHeight) - if (!(plonger = plonger->pprev)) - return error("Reorganize() : plonger->pprev is null"); - if (pfork == plonger) - break; - if (!(pfork = pfork->pprev)) - return error("Reorganize() : pfork->pprev is null"); - } - - // List of what to disconnect - vector vDisconnect; - for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) - vDisconnect.push_back(pindex); + LOCK(cs_main); + CTxDB txdb; - // List of what to connect - vector vConnect; - for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) - vConnect.push_back(pindex); - reverse(vConnect.begin(), vConnect.end()); + auto mapItem = mapBlockIndex.find(NewHash); + if(mapItem == mapBlockIndex.end()) + return error("ForceReorganizeToHash: failed to find requested block in block index"); - printf("REORGANIZE: Disconnect %" PRIszu " blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); - printf("REORGANIZE: Connect %" PRIszu " blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); + CBlockIndex* pindexCur = pindexBest; + CBlockIndex* pindexNew = mapItem->second; + printf("\r\n** Force Reorganize **\r\n"); + printf(" Current best height %i hash %s\n", pindexCur->nHeight,pindexCur->GetBlockHash().GetHex().c_str()); + printf(" Target height %i hash %s\n", pindexNew->nHeight,pindexNew->GetBlockHash().GetHex().c_str()); - if (!vDisconnect.empty()) + CBlock blockNew; + if (!blockNew.ReadFromDisk(pindexNew)) { - //Block was disconnected - User is Re-eligibile for staking - - StructCPID sMag = GetInitializedStructCPID2(GlobalCPUMiningCPID.cpid,mvMagnitudes); + printf("ForceReorganizeToHash: Fatal Error while reading new best block.\r\n"); + return false; + } - if (sMag.initialized) - { - sMag.LastPaymentTime = 0; - mvMagnitudes[GlobalCPUMiningCPID.cpid]=sMag; - } - nLastBlockSolved = 0; + //Re-process the last block to trigger orphan and shit + if (!SetBestChain(txdb, blockNew, pindexNew)) + { + return error("ForceReorganizeToHash Fatal Error while setting best chain.\r\n"); } - printf("REORGANIZE Disc Size %" PRIszu, vDisconnect.size()); - // Disconnect shorter branch - list vResurrect; + AskForOutstandingBlocks(uint256(0)); + printf("ForceReorganizeToHash: success! height %f hash %s\n\n",(double)pindexBest->nHeight,pindexBest->GetBlockHash().GetHex().c_str()); + return true; +} + +bool DisconnectBlocksBatch(CTxDB& txdb, list& vResurrect, unsigned& cnt_dis, CBlockIndex* pcommon) +{ set vRereadCPIDs; - for( CBlockIndex* pindex : vDisconnect ) + while(pindexBest != pcommon) { + if(!pindexBest->pprev) + return error("DisconnectBlocksBatch: attempt to reorganize beyond genesis"); /*fatal*/ + + if (fDebug) printf("DisconnectBlocksBatch: %s\n",pindexBest->GetBlockHash().GetHex().c_str()); + CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("Reorganize() : ReadFromDisk for disconnect failed"); - if (!block.DisconnectBlock(txdb, pindex)) - return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + if (!block.ReadFromDisk(pindexBest)) + return error("DisconnectBlocksBatch: ReadFromDisk for disconnect failed"); /*fatal*/ + if (!block.DisconnectBlock(txdb, pindexBest)) + return error("DisconnectBlocksBatch: DisconnectBlock %s failed", pindexBest->GetBlockHash().ToString().c_str()); /*fatal*/ + + if (!txdb.WriteHashBestChain(pindexBest->GetBlockHash())) + return error("DisconnectBlocksBatch: WriteHashBestChain failed"); /*fatal*/ + + // disconnect from memory + assert(!pindexBest->pnext); + if (pindexBest->pprev) + pindexBest->pprev->pnext = NULL; // Queue memory transactions to resurrect. // We only do this for blocks after the last checkpoint (reorganisation before that // point should only happen with -reindex/-loadblock, or a misbehaving peer. for (auto const& tx : boost::adaptors::reverse(block.vtx)) - if (!(tx.IsCoinBase() || tx.IsCoinStake()) && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) + if (!(tx.IsCoinBase() || tx.IsCoinStake()) && pindexBest->nHeight > Checkpoints::GetTotalBlocksEstimate()) vResurrect.push_front(tx); - // remeber the cpid to re-read later - vRereadCPIDs.insert(pindex->GetCPID()); - } + if(pindexBest->IsUserCPID()) + { + // remeber the cpid to re-read later + vRereadCPIDs.insert(pindexBest->GetCPID()); + // The user has no longer staked this block. + RemoveCPIDBlockHash(pindexBest->GetCPID(), pindexBest->GetBlockHash()); + } + + // New best block + cnt_dis++; + pindexBest = pindexBest->pprev; + hashBestChain = pindexBest->GetBlockHash(); + blockFinder.Reset(); + nBestHeight = pindexBest->nHeight; + nBestChainTrust = pindexBest->nChainTrust; - // Re-read researchers history after all blocks disconnected - for( const string& sRereadCPID : vRereadCPIDs ) - { - StructCPID stCPID = GetLifetimeCPID(sRereadCPID,"DisconnectBlock()"); - (void)stCPID; } - // Connect longer branch - vector vDelete; - for (unsigned int i = 0; i < vConnect.size(); i++) + /* fix up after disconnecting, prepare for new blocks */ + if(cnt_dis>0) { - CBlockIndex* pindex = vConnect[i]; - CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("Reorganize() : ReadFromDisk for connect failed"); - if (!block.ConnectBlock(txdb, pindex, false, true)) + + //Block was disconnected - User is Re-eligibile for staking + StructCPID sMag = GetInitializedStructCPID2(GlobalCPUMiningCPID.cpid,mvMagnitudes); + nLastBlockSolved = 0; + if (sMag.initialized) { - // Invalid block - return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + sMag.LastPaymentTime = 0; + mvMagnitudes[GlobalCPUMiningCPID.cpid]=sMag; } - // Queue memory transactions to delete - for( const CTransaction& tx : block.vtx ) - vDelete.push_back(tx); - } + // Resurrect memory transactions that were in the disconnected branch + for( CTransaction& tx : vResurrect) + AcceptToMemoryPool(mempool, tx, NULL); - if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) - return error("Reorganize() : WriteHashBestChain failed"); + if (!txdb.TxnCommit()) + return error("DisconnectBlocksBatch: TxnCommit failed"); /*fatal*/ - // Make sure it's successfully written to disk before changing memory structure - if (!txdb.TxnCommit()) - return error("Reorganize() : TxnCommit failed"); + // Need to reload all contracts + if (fDebug10) printf("DisconnectBlocksBatch: LoadAdminMessages\n"); + std::string admin_messages; + LoadAdminMessages(true, admin_messages); - // Disconnect shorter branch - for( CBlockIndex* pindex : vDisconnect) - { - if (pindex->pprev) - pindex->pprev->pnext = NULL; - } - - // Connect longer branch - for( CBlockIndex* pindex : vConnect) - { - if (pindex->pprev) - pindex->pprev->pnext = pindex; - } + // Tally research averages. + if(IsV9Enabled_Tally(nBestHeight)) + { + assert(0==(pcommon->nHeight % TALLY_GRANULARITY)); + if (fDebug) printf("DisconnectBlocksBatch: TallyNetworkAverages (v9P %%%d) height %d\n",TALLY_GRANULARITY,nBestHeight); + TallyNetworkAverages_v9(); + } + else + { + // todo: do something with retired tally? maybe? + } - // Resurrect memory transactions that were in the disconnected branch - for( CTransaction& tx : vResurrect) - AcceptToMemoryPool(mempool, tx, NULL); + // Re-read researchers history after all blocks disconnected + if (fDebug10) printf("DisconnectBlocksBatch: GetLifetimeCPID\n"); + for( const string& sRereadCPID : vRereadCPIDs ) + GetLifetimeCPID(sRereadCPID,"DisconnectBlocksBatch"); - // Delete redundant memory transactions that are in the connected branch - for( CTransaction& tx : vDelete) - { - mempool.remove(tx); - mempool.removeConflicts(tx); } - - // Gridcoin: Now that the chain is back in order, Fix the researchers who were disrupted: - - printf("REORGANIZE: done\n"); return true; } - -bool ForceReorganizeToHash(uint256 NewHash) +bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock &blockNew, CBlockIndex* pindexNew) { - CTxDB txdb; - - auto mapItem = mapBlockIndex.find(NewHash); - if(mapItem == mapBlockIndex.end()) - return error("ForceReorganizeToHash: failed to find requested block in block index"); + assert(pindexNew); + //assert(!pindexNew->pnext); + //assert(pindexBest || hashBestChain == pindexBest->GetBlockHash()); + //assert(nBestHeight = pindexBest->nHeight && nBestChainTrust == pindexBest->nChainTrust); + //assert(!pindexBest->pnext); + assert(pindexNew->GetBlockHash()==blockNew.GetHash()); + /* note: it was already determined that this chain is better than current best */ + /* assert(pindexNew->nChainTrust > nBestChainTrust); but may be overriden by command */ + assert( !pindexGenesisBlock == !pindexBest ); - CBlockIndex* pindexCur = pindexBest; - CBlockIndex* pindexNew = mapItem->second; - printf("\r\n** Force Reorganize **\r\n"); - printf(" Current best height %i hash %s\n", pindexCur->nHeight,pindexCur->GetBlockHash().GetHex().c_str()); - printf(" Target height %i hash %s\n", pindexNew->nHeight,pindexNew->GetBlockHash().GetHex().c_str()); + list vResurrect; + list vConnect; + set vRereadCPIDs; - CBlock blockNew; - if (!blockNew.ReadFromDisk(pindexNew)) + /* find fork point */ + CBlockIndex *pcommon = NULL; + if(pindexGenesisBlock) { - printf("ForceReorganizeToHash: Fatal Error while reading new best block.\r\n"); - return false; + pcommon = pindexNew; + while( pcommon->pnext==NULL && pcommon!=pindexBest ) + { + pcommon = pcommon->pprev; + + if(!pcommon) + return error("ReorganizeChain: unable to find fork root"); + } + + if(pcommon!=pindexBest) while( (pcommon->nHeight % TALLY_GRANULARITY)!=0 ) + { + pcommon = pcommon->pprev; + if(!pcommon) + return error("ReorganizeChain: unable to find fork root with tally point"); + } + + if (pcommon!=pindexBest || pindexNew->pprev!=pcommon) + { + printf("\nReorganizeChain: from {%s %d}\n" + "ReorganizeChain: comm {%s %d}\n" + "ReorganizeChain: to {%s %d}\n" + "REORGANIZE: disconnect %d, connect %d blocks\n" + ,pindexBest->GetBlockHash().GetHex().c_str(), pindexBest->nHeight + ,pcommon->GetBlockHash().GetHex().c_str(), pcommon->nHeight + ,pindexNew->GetBlockHash().GetHex().c_str(), pindexNew->nHeight + ,pindexBest->nHeight - pcommon->nHeight + ,pindexNew->nHeight - pcommon->nHeight); + } } - //Re-process the last block to trigger orphan and shit - if (!blockNew.SetBestChain(txdb, pindexNew)) + /* disconnect blocks */ + if(pcommon!=pindexBest) { - return error("ForceReorganizeToHash Fatal Error while setting best chain.\r\n"); + if (!txdb.TxnBegin()) + return error("ReorganizeChain: TxnBegin failed"); + if(!DisconnectBlocksBatch(txdb, vResurrect, cnt_dis, pcommon)) + { + error("ReorganizeChain: DisconnectBlocksBatch() failed"); + printf("This is fatal error. Chain index may be corrupt. Aborting.\n" + "Please Reindex the chain and Restart.\n"); + exit(1); //todo + } } - AskForOutstandingBlocks(uint256(0)); - printf("ForceReorganizeToHash: success! height %f hash %s\n\n",(double)pindexBest->nHeight,pindexBest->GetBlockHash().GetHex().c_str()); - return true; -} + if (fDebug && cnt_dis>0) printf("ReorganizeChain: disconnected %d blocks\n",cnt_dis); -// Called from inside SetBestChain: attaches a block to the new best chain being built -bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew, bool fReorganizing) -{ - uint256 hash = GetHash(); + for(CBlockIndex *p = pindexNew; p != pcommon; p=p->pprev) + vConnect.push_front(p); - // Adding to current best branch - if (!ConnectBlock(txdb, pindexNew, false, fReorganizing) || !txdb.WriteHashBestChain(hash)) + /* Connect blocks */ + for(auto const pindex : vConnect) { - txdb.TxnAbort(); - if (fDebug3) printf("Invalid Chain Found. Invalid block %s\r\n",hash.GetHex().c_str()); - InvalidChainFound(pindexNew); - return false; - } - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - - // Add to current best branch - pindexNew->pprev->pnext = pindexNew; + CBlock block_load; + CBlock &block = (pindex==pindexNew)? blockNew : block_load; - // Delete redundant memory transactions - for (auto const& tx : vtx) - mempool.remove(tx); + if(pindex!=pindexNew) + { + if (!block.ReadFromDisk(pindex)) + return error("ReorganizeChain: ReadFromDisk for connect failed"); + assert(pindex->GetBlockHash()==block.GetHash()); + } + else + { + assert(pindex==pindexNew); + assert(pindexNew->GetBlockHash()==block.GetHash()); + assert(pindexNew->GetBlockHash()==blockNew.GetHash()); + } - return true; -} + uint256 hash = block.GetHash(); + uint256 nBestBlockTrust; -bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) -{ - uint256 hash = GetHash(); + if (fDebug) printf("ReorganizeChain: connect %s\n",hash.ToString().c_str()); - if (!txdb.TxnBegin()) - return error("SetBestChain() : TxnBegin failed"); + if (!txdb.TxnBegin()) + return error("ReorganizeChain: TxnBegin failed"); - if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) - { - txdb.WriteHashBestChain(hash); - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - pindexGenesisBlock = pindexNew; - } - else if (hashPrevBlock == hashBestChain) - { - if (!SetBestChainInner(txdb, pindexNew, false)) + if (pindexGenesisBlock == NULL) { - //int nResult = 0; - return error("SetBestChain() : SetBestChainInner failed"); + if(hash != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + { + txdb.TxnAbort(); + return error("ReorganizeChain: genesis block hash does not match"); + } + pindexGenesisBlock = pindex; } - } - else - { - // the first block in the new chain that will cause it to become the new best chain - CBlockIndex *pindexIntermediate = pindexNew; - - // list of blocks that need to be connected afterwards - std::vector vpindexSecondary; - printf("\r\n**Reorganize**"); - - // Reorganize is costly in terms of db load, as it works in a single db transaction. - // Try to limit how much needs to be done inside - while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust) + else { - vpindexSecondary.push_back(pindexIntermediate); - pindexIntermediate = pindexIntermediate->pprev; + assert(pindex->GetBlockHash()==block.GetHash()); + assert(pindex->pprev == pindexBest); + if (!block.ConnectBlock(txdb, pindex, false, false)) + { + txdb.TxnAbort(); + error("ReorganizeChain: ConnectBlock %s failed", hash.ToString().c_str()); + printf("Previous block %s\n",pindex->pprev->GetBlockHash().ToString().c_str()); + InvalidChainFound(pindex); + return false; + } } - if (!vpindexSecondary.empty()) - printf("Postponing %" PRIszu " reconnects\n", vpindexSecondary.size()); + // Delete redundant memory transactions + for (auto const& tx : block.vtx) + { + mempool.remove(tx); + mempool.removeConflicts(tx); + } - if (!Reorganize(txdb, pindexIntermediate)) + if (!txdb.WriteHashBestChain(pindex->GetBlockHash())) { - printf("Reorganize failed"); txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return error("SetBestChain() : Reorganize failed"); + return error("ReorganizeChain: WriteHashBestChain failed"); } - // Switch to new best branch - // Connect further blocks - std::set connected_cpids; - for (auto &pindex : boost::adaptors::reverse(vpindexSecondary)) + // Make sure it's successfully written to disk before changing memory structure + if (!txdb.TxnCommit()) + return error("ReorganizeChain: TxnCommit failed"); + + // Add to current best branch + if(pindex->pprev && pindexBest && pindexBest->pprev) { - CBlock block; - if (!block.ReadFromDisk(pindex)) + assert( !pindex->pprev->pnext ); + pindex->pprev->pnext = pindex; + nBestBlockTrust = pindexBest->nChainTrust - pindexBest->pprev->nChainTrust; + } + else + nBestBlockTrust = pindex->nChainTrust; + + // update best block + hashBestChain = hash; + pindexBest = pindex; + blockFinder.Reset(); + nBestHeight = pindexBest->nHeight; + nBestChainTrust = pindexBest->nChainTrust; + nTimeBestReceived = GetAdjustedTime(); + cnt_con++; + + // Load recent contracts + std::string admin_messages; + LoadAdminMessages(false, admin_messages); + + if(IsV9Enabled_Tally(nBestHeight)) + { + // quorum not needed + // Tally research averages. + if ((nBestHeight % TALLY_GRANULARITY) == 0) { - printf("SetBestChain() : ReadFromDisk failed\n"); - break; - } - if (!txdb.TxnBegin()) { - printf("SetBestChain() : TxnBegin 2 failed\n"); - break; + if (fDebug) printf("ReorganizeChain: TallyNetworkAverages (v9N %%%d) height %d\n",TALLY_GRANULARITY,nBestHeight); + TallyNetworkAverages_v9(); } + } + else + { + //TODO: do something with retired tally? + } - // errors now are not fatal, we still did a reorganisation to a new chain in a valid way - if (!block.SetBestChainInner(txdb, pindex, true)) - break; + if(pindex->IsUserCPID()) // is this needed? + GetLifetimeCPID(pindex->cpid.GetHex(), "ReorganizeChain"); + } - if(pindex->IsUserCPID()) - connected_cpids.emplace(pindex->cpid); - } + if (fDebug && (cnt_dis>0 || cnt_con>1)) + printf("ReorganizeChain: Disconnected %d and Connected %d blocks.\n",cnt_dis,cnt_con); - // Retally after reorganize to sync up amounts owed. - BusyWaitForTally_retired(); - TallyNetworkAverages_v9(); + return true; +} - // Recalculate amounts paid. - for(const auto& cpid : connected_cpids) - GetLifetimeCPID(cpid.GetHex(), "SetBestChain()"); +bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew) +{ + unsigned cnt_dis=0; + unsigned cnt_con=0; + bool success = false; + const auto prevTrust = nBestChainTrust; + + success = ReorganizeChain(txdb, cnt_dis, cnt_con, blockNew, pindexNew); + + if(!success) + return false; + if(!success || prevTrust>nBestChainTrust) + { + /* + printf("SetBestChain: Reorganize caused lower chain trust than before. Reorganizing back.\n"); + success = ReorganizeChain(txdb, cnt_dis, cnt_con, blockNew, pindexNew); + + printf("SetBestChain: Reorganize caused lower chain trust than before. Reorganizing back.\n"); + success = ReorganizeChain(txdb, cnt_dis, cnt_con, blockNew, pindexNew); + */ } + /* Fix up after block connecting */ + + + //std::set connected_cpids; + + // Update best block in wallet (so we can detect restored wallets) bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload) @@ -3633,26 +3711,35 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) ::SetBestChain(locator); } - // New best block - hashBestChain = hash; - pindexBest = pindexNew; - blockFinder.Reset(); - nBestHeight = pindexBest->nHeight; - nBestChainTrust = pindexNew->nChainTrust; - nTimeBestReceived = GetAdjustedTime(); + // Retally after reorganize to sync up amounts owed. + if (!fIsInitialDownload) + BusyWaitForTally_retired(); - uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust; + if(IsV9Enabled_Tally(nBestHeight)) + { + // Update quorum data. + if ((nBestHeight % 3) == 0) + { + if (fDebug) printf("SetBestChain: Updating Neural Supermajority (v9 %%3) height %d\n",nBestHeight); + ComputeNeuralNetworkSupermajorityHashes(); + } + // Update quorum data. + if ((nBestHeight % 10) == 0 && !OutOfSyncByAge() && NeedASuperblock()) + { + if (fDebug) printf("SetBestChain: Updating Neural Quorum (v9 M) height %d\n",nBestHeight); + UpdateNeuralNetworkQuorumData(); + } + } if (fDebug) { - printf("{SBC} SetBestChain: new best=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, - CBigNum(nBestChainTrust).ToString().c_str(), - nBestBlockTrust.Get64(), - DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + printf("{SBC} {%s %d} trust=%s date=%s\n", + hashBestChain.ToString().c_str(), nBestHeight, + CBigNum(nBestChainTrust).ToString().c_str(), + DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); } else - printf("{SBC} new best=%s height=%d ; ",hashBestChain.ToString().c_str(), nBestHeight); + printf("{SBC} new best {%s %d} ; ",hashBestChain.ToString().c_str(), nBestHeight); std::string strCmd = GetArg("-blocknotify", ""); if (!fIsInitialDownload && !strCmd.empty()) @@ -3663,6 +3750,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // Perform Gridcoin services now that w have a new head. // Remove V9 checks after the V9 switch. + // TODO: ??? if(IsV9Enabled(nBestHeight)) GridcoinServices(); @@ -3794,7 +3882,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u // New best if (pindexNew->nChainTrust > nBestChainTrust) - if (!SetBestChain(txdb, pindexNew)) + if (!SetBestChain(txdb, *this, pindexNew)) return false; if (pindexNew == pindexBest) @@ -3875,7 +3963,7 @@ bool CBlock::CheckBlock(std::string sCaller, int height1, int64_t Mint, bool fCh LoadAdminMessages(false,sOut2); if (!fLoadingIndex && !IsCPIDValidv2(bb,height1)) { - return error("Bad CPID or Block Signature : height %i, CPID %s, cpidv2 %s, LBH %s, Bad Hashboinc %s", height1, + return error("Bad CPID or Block Signature : height %i, CPID %s, cpidv2 %s, LBH %s, Bad Hashboinc [%s]", height1, bb.cpid.c_str(), bb.cpidv2.c_str(), bb.lastblockhash.c_str(), vtx[0].hashBoinc.c_str()); } @@ -3936,11 +4024,6 @@ bool CBlock::CheckBlock(std::string sCaller, int height1, int64_t Mint, bool fCh // ppcoin: check transaction timestamp if (GetBlockTime() < (int64_t)tx.nTime) return DoS(50, error("CheckBlock[] : block timestamp earlier than transaction timestamp")); - - // Verify beacon contract if a transaction contains a beacon contract - // Current bad contracts in chain would cause a fork on sync, skip them - if (nVersion>=9 && !VerifyBeaconContractTx(tx.hashBoinc) && !fLoadingIndex) - return DoS(25, error("CheckBlock[] : bad beacon contract found in tx %s contained within block; rejected", tx.GetHash().ToString().c_str())); } // Check for duplicate txids. This is caught by ConnectInputs(), @@ -4019,12 +4102,18 @@ bool CBlock::AcceptBlock(bool generated_by_me) return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake")); } - - // Check that all transactions are finalized for (auto const& tx : vtx) + { + // Check that all transactions are finalized if (!IsFinalTx(tx, nHeight, GetBlockTime())) return DoS(10, error("AcceptBlock() : contains a non-final transaction")); + // Verify beacon contract if a transaction contains a beacon contract + // Current bad contracts in chain would cause a fork on sync, skip them + if (nVersion>=9 && !VerifyBeaconContractTx(tx)) + return DoS(25, error("CheckBlock[] : bad beacon contract found in tx %s contained within block; rejected", tx.GetHash().ToString().c_str())); + } + // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckHardened(nHeight, hash)) return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight)); @@ -4248,34 +4337,7 @@ void GridcoinServices() if(IsV9Enabled_Tally(nBestHeight)) { - // Update quorum data. - if ((nBestHeight % 3) == 0) - { - if (fDebug) printf("SVC: Updating Neural Supermajority (v9 %%3) height %d\n",nBestHeight); - ComputeNeuralNetworkSupermajorityHashes(); - } - // Update quorum data. - if ((nBestHeight % 10) == 0 && !OutOfSyncByAge() && NeedASuperblock()) - { - if (fDebug) printf("SVC: Updating Neural Quorum (v9 M) height %d\n",nBestHeight); - UpdateNeuralNetworkQuorumData(); - } - - // Tally research averages. - if ((nBestHeight % TALLY_GRANULARITY) == 0) - { - // Wait for previous retired tally to finish if running. - // This can happen when syncing the chain. - if(!bTallyFinished_retired) - { - printf("SVC: Wait for retired tally to finish\n"); - while(!bTallyFinished_retired) - MilliSleep(10); - } - - if (fDebug) printf("SVC: TallyNetworkAverages (v9 %%%d) height %d\n",TALLY_GRANULARITY,nBestHeight); - TallyNetworkAverages_v9(); - } + // in SetBestChain } else { @@ -5068,8 +5130,8 @@ bool IsCPIDValidv2(MiningCPID& mc, int height) } else if (height >= cpidV3CutOverHeight) { - if (mc.cpid.empty()) return false; - if (!IsResearcher(mc.cpid)) return true; + if (mc.cpid.empty()) return error("IsCPIDValidv2(): cpid empty"); + if (!IsResearcher(mc.cpid)) return true; /* is investor? */ // V3 requires a beacon, a beacon public key and a valid block signature signed by the CPID's private key result = VerifyCPIDSignature(mc.cpid,mc.lastblockhash,mc.BoincSignature); } @@ -5314,7 +5376,12 @@ HashSet GetCPIDBlockHashes(const std::string& cpid) void AddCPIDBlockHash(const std::string& cpid, const uint256& blockhash) { // Add block hash to CPID hash set. - mvCPIDBlockHashes[cpid].insert(blockhash); + mvCPIDBlockHashes[cpid].emplace(blockhash); +} + +void RemoveCPIDBlockHash(const std::string& cpid, const uint256& blockhash) +{ + mvCPIDBlockHashes[cpid].erase(blockhash); } StructCPID GetLifetimeCPID(const std::string& cpid, const std::string& sCalledFrom) @@ -5351,6 +5418,10 @@ StructCPID GetLifetimeCPID(const std::string& cpid, const std::string& sCalledFr if (fDebug10) printf("GetLifetimeCPID: verified %s height= %d LastBlock= %d nResearchSubsidy= %.3f\n", uHash.GetHex().c_str(),pblockindex->nHeight,(int)stCPID.LastBlock,pblockindex->nResearchSubsidy); + if(!pblockindex->pnext && pblockindex!=pindexBest) + printf("WARNING GetLifetimeCPID: index {%s %d} for cpid %s, " + "is not in the main chain\n",pblockindex->GetBlockHash().GetHex().c_str(), + pblockindex->nHeight,cpid.c_str()); if (pblockindex->nHeight > stCPID.LastBlock && pblockindex->nResearchSubsidy > 0) { stCPID.LastBlock = pblockindex->nHeight; @@ -5749,6 +5820,14 @@ bool TallyNetworkAverages_v9() { if (IsResearchAgeEnabled(pindexBest->nHeight) && IsV9Enabled_Tally(pindexBest->nHeight)) { + if(!bTallyFinished_retired) + { + // Wait for previous retired tally to finish if running. + // This can happen when syncing the chain. + printf("TallyNetworkAverages_v9: Wait for retired tally to finish\n"); + while(!bTallyFinished_retired) + MilliSleep(10); + } return TallyResearchAverages_v9(); } diff --git a/src/main.h b/src/main.h index ed433f841d..759598f694 100755 --- a/src/main.h +++ b/src/main.h @@ -304,6 +304,8 @@ StructCPID GetInitializedStructCPID2(const std::string& name, std::map