diff --git a/src/Makefile.am b/src/Makefile.am index fd45ae98f6..bc935b36c6 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,7 @@ GRIDCOIN_CORE_H = \ mruset.h \ netbase.h \ net.h \ + node/blockstorage.h \ pbkdf2.h \ policy/fees.h \ policy/policy.h \ @@ -239,6 +240,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ miner.cpp \ netbase.cpp \ net.cpp \ + node/blockstorage.cpp \ noui.cpp \ pbkdf2.cpp \ policy/policy.cpp \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0d7b4cc5a7..88c09f3e7b 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -64,6 +64,9 @@ class CMainParams : public CChainParams { consensus.BlockV9TallyHeight = 1144120; consensus.BlockV10Height = 1420000; consensus.BlockV11Height = 2053000; + // "standard" scrypt target limit for proof of work, results in 0,000244140625 proof-of-work difficulty. + // Equivalent to ~arith_uint256() >> 20 or 1e0fffff in compact notation. + consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -146,6 +149,8 @@ class CTestNetParams : public CChainParams { consensus.BlockV9TallyHeight = 399120; consensus.BlockV10Height = 629409; consensus.BlockV11Height = 1301500; + // Equivalent to ~arith_uint256() >> 16 or 1f00ffff in compact notation. + consensus.powLimit = uint256S("0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); pchMessageStart[0] = 0xcd; pchMessageStart[1] = 0xf2; diff --git a/src/consensus/params.h b/src/consensus/params.h index 033cbdf31a..ba0a8c305b 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -29,5 +29,7 @@ struct Params { int BlockV10Height; /** Block height at which v11 blocks are created */ int BlockV11Height; + + uint256 powLimit; }; } // namespace Consensus diff --git a/src/gridcoin/accrual/snapshot.h b/src/gridcoin/accrual/snapshot.h index 4f28f9e661..c01b7c00de 100644 --- a/src/gridcoin/accrual/snapshot.h +++ b/src/gridcoin/accrual/snapshot.h @@ -6,6 +6,7 @@ #include "amount.h" #include "arith_uint256.h" +#include "chainparams.h" #include "fs.h" #include "gridcoin/account.h" #include "gridcoin/accrual/computer.h" @@ -13,6 +14,7 @@ #include "gridcoin/cpid.h" #include "gridcoin/superblock.h" #include "gridcoin/support/filehash.h" +#include "node/blockstorage.h" #include "serialize.h" #include "streams.h" #include "tinyformat.h" @@ -1638,7 +1640,7 @@ class SnapshotBaselineBuilder CBlock block; - if (!block.ReadFromDisk(pindex)) { + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { return error( "SnapshotBaselineBuilder: failed to load superblock %" PRIu64, pindex->nHeight); diff --git a/src/gridcoin/contract/contract.cpp b/src/gridcoin/contract/contract.cpp index 6444833e81..2e0cee2966 100644 --- a/src/gridcoin/contract/contract.cpp +++ b/src/gridcoin/contract/contract.cpp @@ -3,6 +3,7 @@ // file COPYING or https://opensource.org/licenses/mit-license.php. #include "amount.h" +#include "chainparams.h" #include "main.h" #include "gridcoin/appcache.h" #include "gridcoin/claim.h" @@ -16,6 +17,7 @@ #include "gridcoin/tx_message.h" #include "gridcoin/voting/payloads.h" #include "gridcoin/voting/registry.h" +#include "node/blockstorage.h" #include "util.h" #include "wallet/wallet.h" @@ -457,7 +459,7 @@ void GRC::ReplayContracts(CBlockIndex* pindex_end, CBlockIndex* pindex_start) // have to be checked, OR the block index entry is already marked to contain contract(s), // then apply the contracts found in the block. if (beacons.NeedsIsContractCorrection() || pindex->IsContract()) { - if (!block.ReadFromDisk(pindex)) { + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { continue; } @@ -490,7 +492,7 @@ void GRC::ReplayContracts(CBlockIndex* pindex_end, CBlockIndex* pindex_start) if (pindex->IsSuperblock() && pindex->nVersion >= 11) { if (block.hashPrevBlock != pindex->pprev->GetBlockHash() - && !block.ReadFromDisk(pindex)) + && !ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { continue; } diff --git a/src/gridcoin/quorum.cpp b/src/gridcoin/quorum.cpp index a252d5211c..9873f131c3 100644 --- a/src/gridcoin/quorum.cpp +++ b/src/gridcoin/quorum.cpp @@ -3,12 +3,14 @@ // file COPYING or https://opensource.org/licenses/mit-license.php. #include "base58.h" +#include "chainparams.h" #include "main.h" #include "gridcoin/claim.h" #include "gridcoin/magnitude.h" #include "gridcoin/quorum.h" #include "gridcoin/scraper/scraper_net.h" #include "gridcoin/superblock.h" +#include "node/blockstorage.h" #include "util/reverse_iterator.h" #include @@ -458,7 +460,7 @@ class LegacyConsensus while (pindex && pindex->nHeight > min_height) { CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); if (block.GetClaim().m_quorum_hash.Valid()) { Claim claim = block.PullClaim(); diff --git a/src/gridcoin/staking/difficulty.cpp b/src/gridcoin/staking/difficulty.cpp index cd7896c296..5303197ca4 100644 --- a/src/gridcoin/staking/difficulty.cpp +++ b/src/gridcoin/staking/difficulty.cpp @@ -5,11 +5,13 @@ #include "amount.h" #include "bignum.h" +#include "chainparams.h" #include "init.h" #include "gridcoin/staking/difficulty.h" #include "gridcoin/staking/kernel.h" #include "gridcoin/staking/status.h" #include "main.h" +#include "node/blockstorage.h" #include "txdb.h" #include "wallet/wallet.h" @@ -367,7 +369,8 @@ double GRC::GetEstimatedTimetoStake(bool ignore_staking_status, double dDiff, do CBlock CoinBlock; //Block which contains CoinTx if (!txdb.ReadTxIndex(out.tx->GetHash(), txindex)) continue; //Ignore transactions that can't be read. - if (!CoinBlock.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) continue; + if (!ReadBlockFromDisk(CoinBlock, txindex.pos.nFile, txindex.pos.nBlockPos, Params().GetConsensus(), false)) + continue; // We are going to store as an event the time that the UTXO matures (is available for staking again.) nTime = (CoinBlock.GetBlockTime() & ~ETTS_TIMESTAMP_MASK) + nStakeMinAge; diff --git a/src/gridcoin/superblock.cpp b/src/gridcoin/superblock.cpp index 5bdcdff3ea..43abbce8a7 100644 --- a/src/gridcoin/superblock.cpp +++ b/src/gridcoin/superblock.cpp @@ -2,11 +2,13 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "compat/endian.h" #include "hash.h" #include "main.h" #include "gridcoin/superblock.h" #include "gridcoin/support/xml.h" +#include "node/blockstorage.h" #include "sync.h" #include "util.h" #include "util/reverse_iterator.h" @@ -981,7 +983,7 @@ SuperblockPtr SuperblockPtr::ReadFromDisk(const CBlockIndex* const pindex) CBlock block; - if (!block.ReadFromDisk(pindex)) { + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { error("%s: failed to read superblock from disk", __func__); return Empty(); } diff --git a/src/gridcoin/voting/builders.cpp b/src/gridcoin/voting/builders.cpp index 18eb690546..6c13fb4d31 100644 --- a/src/gridcoin/voting/builders.cpp +++ b/src/gridcoin/voting/builders.cpp @@ -3,6 +3,7 @@ // file COPYING or https://opensource.org/licenses/mit-license.php. #include "amount.h" +#include "chainparams.h" #include "init.h" #include "main.h" #include "gridcoin/beacon.h" @@ -13,6 +14,7 @@ #include "gridcoin/voting/claims.h" #include "gridcoin/voting/payloads.h" #include "gridcoin/voting/registry.h" +#include "node/blockstorage.h" #include "ui_interface.h" #include "wallet/wallet.h" #include @@ -594,7 +596,7 @@ class MagnitudeClaimBuilder continue; } - if (!block.ReadFromDisk(pindex)) { + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { break; } diff --git a/src/gridcoin/voting/registry.cpp b/src/gridcoin/voting/registry.cpp index 2a2af12fc3..de84f2b878 100644 --- a/src/gridcoin/voting/registry.cpp +++ b/src/gridcoin/voting/registry.cpp @@ -3,6 +3,7 @@ // file COPYING or https://opensource.org/licenses/mit-license.php. #include "amount.h" +#include "chainparams.h" #include "main.h" #include "gridcoin/claim.h" #include "gridcoin/researcher.h" @@ -12,6 +13,7 @@ #include "gridcoin/voting/registry.h" #include "gridcoin/voting/vote.h" #include "gridcoin/support/block_finder.h" +#include "node/blockstorage.h" #include "txdb.h" #include "ui_interface.h" #include "validation.h" @@ -696,7 +698,7 @@ const PollReference* PollRegistry::TryByTxidWithAddHistoricalPollAndVotes(const // a valid vote. for (CBlockIndex* pindex = pindex_poll; pindex; pindex = pindex->pnext) { // If the block doesn't contain contract(s) or can't read, skip. - if (!pindex->IsContract() || !block.ReadFromDisk(pindex, true)) continue; + if (!pindex->IsContract() || !ReadBlockFromDisk(block, pindex, Params().GetConsensus())) continue; // Skip coinbase and coinstake transactions: for (unsigned int i = 2; i < block.vtx.size(); ++i) { diff --git a/src/init.cpp b/src/init.cpp index 18a7f1e5f6..06351d1116 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -16,6 +16,7 @@ #include "scheduler.h" #include "gridcoin/gridcoin.h" #include "miner.h" +#include "node/blockstorage.h" #include #include @@ -1186,7 +1187,7 @@ bool AppInit2(ThreadHandlerPtr threads) { CBlockIndex* pindex = mi->second; CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); block.print(); LogPrintf(""); nFound++; diff --git a/src/main.cpp b/src/main.cpp index 0406a1d3c3..5d84c1b50f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ // file COPYING or https://opensource.org/licenses/mit-license.php. #include "amount.h" +#include "chainparams.h" #include "consensus/merkle.h" #include "util.h" #include "net.h" @@ -30,6 +31,7 @@ #include "gridcoin/support/xml.h" #include "gridcoin/tally.h" #include "gridcoin/tx_message.h" +#include "node/blockstorage.h" #include "policy/fees.h" #include "policy/policy.h" #include "validation.h" @@ -72,9 +74,6 @@ BlockIndexPool::Pool BlockIndexPool::m_researcher_context_poo BlockMap mapBlockIndex; -CBigNum bnProofOfWorkLimit(ArithToUint256(~arith_uint256() >> 20)); // "standard" scrypt target limit for proof of work, results with 0,000244140625 proof-of-work difficulty -CBigNum bnProofOfWorkLimitTestNet(ArithToUint256(~arith_uint256() >> 16)); - //Gridcoin Minimum Stake Age (16 Hours) unsigned int nStakeMinAge = 16 * 60 * 60; // 16 hours unsigned int nStakeMaxAge = -1; // unlimited @@ -336,7 +335,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) CTxIndex txindex; if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) return 0; - if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) + if (!ReadBlockFromDisk(blockTmp, txindex.pos.nFile, txindex.pos.nBlockPos, Params().GetConsensus())) return 0; pblock = &blockTmp; } @@ -689,7 +688,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) if (ReadTxFromDisk(tx, txdb, COutPoint(hash, 0), txindex)) { CBlock block; - if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + if (ReadBlockFromDisk(block, txindex.pos.nFile, txindex.pos.nBlockPos, Params().GetConsensus(), false)) hashBlock = block.GetHash(true); return true; } @@ -706,21 +705,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) // // CBlock and CBlockIndex // -bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) -{ - if (!fReadTransactions) - { - SetNull(); - *(static_cast(this)) = pindex->GetBlockHeader(); - return true; - } - if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) - return false; - if (GetHash(true) != pindex->GetBlockHash()) - return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); - return true; -} - static const CBlock* GetOrphanRoot(const CBlock* pblock) { // Work back to the first block in the orphan chain @@ -729,21 +713,6 @@ static const CBlock* GetOrphanRoot(const CBlock* pblock) return pblock; } -bool CheckProofOfWork(uint256 hash, unsigned int nBits) -{ - CBigNum bnTarget; - bnTarget.SetCompact(nBits); - - // Check range - if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) - return error("CheckProofOfWork() : nBits below minimum work"); - - // Check proof of work matches claimed amount - if (UintToArith256(hash) > UintToArith256(bnTarget.getuint256())) - return error("CheckProofOfWork() : hash doesn't match nBits"); - - return true; -} // Return maximum amount of blocks that other nodes claim to have int GetNumBlocksOfPeers() @@ -1566,7 +1535,7 @@ bool ForceReorganizeToHash(uint256 NewHash) LogPrintf(" Target height %i hash %s", pindexNew->nHeight,pindexNew->GetBlockHash().GetHex()); CBlock blockNew; - if (!blockNew.ReadFromDisk(pindexNew)) + if (!ReadBlockFromDisk(blockNew, pindexNew, Params().GetConsensus())) { LogPrintf("ForceReorganizeToHash: Fatal Error while reading new best block."); return false; @@ -1605,7 +1574,7 @@ bool DisconnectBlocksBatch(CTxDB& txdb, list& vResurrect, unsigned LogPrint(BCLog::LogFlags::VERBOSE, "DisconnectBlocksBatch: %s",pindexBest->GetBlockHash().GetHex()); CBlock block; - if (!block.ReadFromDisk(pindexBest)) + if (!ReadBlockFromDisk(block, pindexBest, Params().GetConsensus())) 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*/ @@ -1790,7 +1759,7 @@ bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock & if(pindex!=pindexNew) { - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) return error("ReorganizeChain: ReadFromDisk for connect failed"); assert(pindex->GetBlockHash()==block.GetHash(true)); } @@ -1889,7 +1858,7 @@ bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew) { LogPrintf("SetBestChain: Reorganize caused lower chain trust than before. Reorganizing back."); CBlock origBlock; - if (!origBlock.ReadFromDisk(origBestIndex)) + if (!ReadBlockFromDisk(origBlock, origBestIndex, Params().GetConsensus())) return error("SetBestChain: Fatal Error while reading original best block"); success = ReorganizeChain(txdb, cnt_dis, cnt_con, origBlock, origBestIndex); } @@ -2024,7 +1993,7 @@ bool CBlock::CheckBlock(int height1, bool fCheckPOW, bool fCheckMerkleRoot, bool } // Check proof of work matches claimed amount - if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(true), nBits)) + if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(true), nBits, Params().GetConsensus())) return DoS(50, error("CheckBlock[] : proof of work failed")); //Reject blocks with diff that has grown to an extraordinary level (should never happen) @@ -2290,7 +2259,7 @@ bool CBlock::AcceptBlock(bool generated_by_me) return error("AcceptBlock() : out of disk space"); unsigned int nFile = -1; unsigned int nBlockPos = 0; - if (!WriteToDisk(nFile, nBlockPos)) + if (!WriteBlockToDisk(*this, nFile, nBlockPos, Params().MessageStart())) return error("AcceptBlock() : WriteToDisk failed"); if (!AddToBlockIndex(nFile, nBlockPos, hashProof)) return error("AcceptBlock() : AddToBlockIndex failed"); @@ -2624,7 +2593,6 @@ bool LoadBlockIndex(bool fAllowNew) if (fTestNet) { // GLOBAL TESTNET SETTINGS - R HALFORD - bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 16 bits PoW target limit for testnet nStakeMinAge = 1 * 60 * 60; // test net min age is 1 hour nCoinbaseMaturity = 10; // test maturity is 10 blocks nGrandfather = 196550; @@ -2684,7 +2652,7 @@ bool LoadBlockIndex(bool fAllowNew) //R&D - Testers Wanted Thread: block.nTime = !fTestNet ? 1413033777 : 1406674534; //Official Launch time: - block.nBits = bnProofOfWorkLimit.GetCompact(); + block.nBits = UintToArith256(Params().GetConsensus().powLimit).GetCompact(); block.nNonce = !fTestNet ? 130208 : 22436; LogPrintf("starting Genesis Check..."); // If genesis block hash does not match, then generate new genesis hash. @@ -2730,7 +2698,7 @@ bool LoadBlockIndex(bool fAllowNew) // Start new block file unsigned int nFile; unsigned int nBlockPos; - if (!block.WriteToDisk(nFile, nBlockPos)) + if (!WriteBlockToDisk(block, nFile, nBlockPos, Params().MessageStart())) return error("LoadBlockIndex() : writing genesis block to disk failed"); if (!block.AddToBlockIndex(nFile, nBlockPos, hashGenesisBlock)) return error("LoadBlockIndex() : genesis block not accepted"); @@ -2790,7 +2758,7 @@ void PrintBlockTree() // print item CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); LogPrintf("%d (%u,%u) %s %08x %s tx %" PRIszu "", pindex->nHeight, pindex->nFile, @@ -3290,7 +3258,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (mi != mapBlockIndex.end()) { CBlock block; - block.ReadFromDisk(mi->second); + ReadBlockFromDisk(block, mi->second, Params().GetConsensus()); pfrom->PushMessage("encrypt", block); @@ -4049,7 +4017,7 @@ GRC::ClaimOption GetClaimByIndex(const CBlockIndex* const pblockindex) CBlock block; if (!pblockindex || !pblockindex->IsInMainChain() - || !block.ReadFromDisk(pblockindex)) + || !ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) { return std::nullopt; } diff --git a/src/main.h b/src/main.h index 103b155514..170c9a4b0b 100644 --- a/src/main.h +++ b/src/main.h @@ -124,7 +124,6 @@ bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); bool LoadExternalBlockFile(FILE* fileIn); -bool CheckProofOfWork(uint256 hash, unsigned int nBits); GRC::ClaimOption GetClaimByIndex(const CBlockIndex* const pblockindex); int GetNumBlocksOfPeers(); @@ -420,59 +419,6 @@ class CBlock : public CBlockHeader return maxTransactionTime; } - bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) - { - // Open history file to append - CAutoFile fileout(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("CBlock::WriteToDisk() : AppendBlockFile failed"); - - // Write index header - unsigned int nSize = GetSerializeSize(fileout, *this); - fileout << Params().MessageStart() << nSize; - - // Write block - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("CBlock::WriteToDisk() : ftell failed"); - nBlockPosRet = fileOutPos; - fileout << *this; - - // Flush stdio buffers and commit to disk before returning - fflush(fileout.Get()); - if (!IsInitialBlockDownload() || (nBestHeight+1) % 5000 == 0) - FileCommit(fileout.Get()); - - return true; - } - - bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) - { - SetNull(); - - const int ser_flags = SER_DISK | (fReadTransactions ? 0 : SER_BLOCKHEADERONLY); - - // Open history file to read - CAutoFile filein(OpenBlockFile(nFile, nBlockPos, "rb"), ser_flags, CLIENT_VERSION); - if (filein.IsNull()) - return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); - - // Read block - try { - filein >> *this; - } - catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - - // Check the header - if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(true), nBits)) - return error("CBlock::ReadFromDisk() : errors in block header"); - - return true; - } - - void print() const { @@ -494,7 +440,6 @@ class CBlock : public CBlockHeader bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false); - bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProof); bool CheckBlock(int height1, bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true, bool fLoadingIndex=false) const; bool AcceptBlock(bool generated_by_me); diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp new file mode 100644 index 0000000000..ded7b6c08d --- /dev/null +++ b/src/node/blockstorage.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2011-2021 The Bitcoin Core developers +// Copyright (c) 2021 The Gridcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chainparams.h" +#include "main.h" +#include "protocol.h" +#include "serialize.h" +#include "validation.h" + +#include + + +bool WriteBlockToDisk(const CBlock& block, unsigned int& nFileRet, unsigned int& nBlockPosRet, + const CMessageHeader::MessageStartChars& messageStart) +{ + // Open history file to append + CAutoFile fileout(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: AppendBlockFile failed", __func__); + + // Write index header + unsigned int nSize = GetSerializeSize(fileout, block); + fileout << messageStart << nSize; + + // Write block + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("%s: ftell failed", __func__); + nBlockPosRet = fileOutPos; + fileout << block; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout.Get()); + if (!IsInitialBlockDownload() || (nBestHeight + 1) % 5000 == 0) + FileCommit(fileout.Get()); + + return true; +} + + +bool ReadBlockFromDisk(CBlock& block, unsigned int nFile, unsigned int nBlockPos, + const Consensus::Params& params, bool fReadTransactions=true) +{ + block.SetNull(); + + const int ser_flags = SER_DISK | (fReadTransactions ? 0 : SER_BLOCKHEADERONLY); + + // Open history file to read + CAutoFile filein(OpenBlockFile(nFile, nBlockPos, "rb"), ser_flags, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + + // Read block + try { + filein >> block; + } + catch (std::exception &e) { + return error("%s: deserialize or I/O error", __func__); + } + + // Check the header + if (fReadTransactions && block.IsProofOfWork() && !CheckProofOfWork(block.GetHash(true), block.nBits, params)) + return error("%s: errors in block header", __func__); + + return true; +} + + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& params, + bool fReadTransactions=true) +{ + if (!fReadTransactions) + { + block.SetNull(); + *(static_cast(&block)) = pindex->GetBlockHeader(); + return true; + } + + if (!ReadBlockFromDisk(block, pindex->nFile, pindex->nBlockPos, params, fReadTransactions)) + return false; + + if (block.GetHash(true) != pindex->GetBlockHash()) + return error("%s: hash doesn't match index (%s != %s)", __func__, block.GetHash(true).GetHex(), + pindex->GetBlockHash().GetHex()); + return true; +} + diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h new file mode 100644 index 0000000000..8bddc16ccc --- /dev/null +++ b/src/node/blockstorage.h @@ -0,0 +1,25 @@ +// Copyright (c) 2011-2021 The Bitcoin Core developers +// Copyright (c) 2021 The Gridcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_BLOCKSTORAGE_H +#define BITCOIN_NODE_BLOCKSTORAGE_H + +#include "protocol.h" + +class CBlock; +class CBlockIndex; + +namespace Consensus { +struct Params; +} + +bool WriteBlockToDisk(const CBlock& block, unsigned int& nFileRet, unsigned int& nBlockPosRet, const CMessageHeader::MessageStartChars& messageStart); + +bool ReadBlockFromDisk(CBlock& block, unsigned int nFile, unsigned int nBlockPos, const Consensus::Params& params, bool fReadTransactions=true); +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& params, bool fReadTransactions=true); + + +#endif // BITCOIN_NODE_BLOCKSTORAGE_H + diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 54315b37cf..e47ebf9dfe 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -3,7 +3,9 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "blockchain.h" +#include "node/blockstorage.h" #include #include @@ -301,7 +303,7 @@ UniValue dumpcontracts(const UniValue& params, bool fHelp) while (pblockindex != nullptr && pblockindex->nHeight <= high_height) { - block.ReadFromDisk(pblockindex); + ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()); bool include_element_in_export = false; @@ -342,7 +344,7 @@ UniValue dumpcontracts(const UniValue& params, bool fHelp) while (pblockindex != nullptr && pblockindex->nHeight <= high_height) { - block.ReadFromDisk(pblockindex); + ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()); bool include_element_in_export = false; @@ -422,7 +424,7 @@ UniValue showblock(const UniValue& params, bool fHelp) if (pblockindex == nullptr) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); CBlock block; - block.ReadFromDisk(pblockindex); + ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()); return blockToJSON(block, pblockindex, false); } @@ -558,7 +560,7 @@ UniValue getblock(const UniValue& params, bool fHelp) CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex, true); + ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()); return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); } @@ -587,7 +589,7 @@ UniValue getblockbynumber(const UniValue& params, bool fHelp) uint256 hash = *pblockindex->phashBlock; pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex, true); + ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()); return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); } @@ -695,7 +697,7 @@ UniValue getblocksbatch(const UniValue& params, bool fHelp) while (i < batch_size) { CBlock block; - if (!block.ReadFromDisk(pblockindex, true)) + if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) { throw runtime_error("Error reading block from specified batch."); } @@ -2258,7 +2260,7 @@ UniValue GetJSONVersionReport(const int64_t lookback, const bool full_version) pindex = pindex->pprev) { CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); std::string version = block.PullClaim().m_client_version; @@ -2355,7 +2357,7 @@ UniValue getburnreport(const UniValue& params, bool fHelp) // be very difficult or expensive to recognize. // for (const CBlockIndex* pindex = pindexGenesisBlock; pindex; pindex = pindex->pnext) { - if (!block.ReadFromDisk(pindex)) { + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { continue; } diff --git a/src/rpc/dataacq.cpp b/src/rpc/dataacq.cpp index a663f351cc..552933eefb 100644 --- a/src/rpc/dataacq.cpp +++ b/src/rpc/dataacq.cpp @@ -4,6 +4,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "main.h" #include "server.h" #include "txdb.h" @@ -12,6 +13,7 @@ #include "gridcoin/staking/difficulty.h" #include "gridcoin/superblock.h" #include "gridcoin/support/block_finder.h" +#include "node/blockstorage.h" #include "util.h" #include @@ -143,7 +145,7 @@ UniValue rpc_getblockstats(const UniValue& params, bool fHelp) blockcount++; CBlock block; - if (!block.ReadFromDisk(cur->nFile,cur->nBlockPos,true)) + if (!ReadBlockFromDisk(block, cur->nFile,cur->nBlockPos, Params().GetConsensus())) { throw runtime_error("getblockstats: failed to read block"); } @@ -419,7 +421,7 @@ UniValue rpc_exportstats(const UniValue& params, bool fHelp) cnt_contract += !! cur->IsContract(); CBlock block; - if(!block.ReadFromDisk(cur->nFile,cur->nBlockPos,true)) + if (!ReadBlockFromDisk(block, cur->nFile, cur->nBlockPos, Params().GetConsensus())) throw runtime_error("failed to read block"); cnt_trans += block.vtx.size()-2; /* 2 transactions are special */ @@ -603,7 +605,7 @@ UniValue rpc_getrecentblocks(const UniValue& params, bool fHelp) if( (detail<100 && detail>=20) || (detail>=120) ) { CBlock block; - if(!block.ReadFromDisk(cur->nFile,cur->nBlockPos,true)) + if(!ReadBlockFromDisk(block, cur->nFile, cur->nBlockPos, Params().GetConsensus())) throw runtime_error("failed to read block"); //assert(block.vtx.size() > 0); const Claim& claim = block.GetClaim(); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index de069ce3bf..a30c20d4ae 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "init.h" #include "main.h" #include "gridcoin/beacon.h" @@ -14,6 +15,7 @@ #include "gridcoin/support/block_finder.h" #include "gridcoin/tx_message.h" #include "gridcoin/voting/payloads.h" +#include "node/blockstorage.h" #include "policy/policy.h" #include "policy/fees.h" #include "primitives/transaction.h" @@ -50,7 +52,7 @@ std::vector> GetTxStakeBoincHashInfo(const C pindex = mi->second; - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { res.push_back(std::make_pair(_("ERROR"), _("Block read failed"))); return res; @@ -1086,7 +1088,7 @@ UniValue consolidatemsunspent(const UniValue& params, bool fHelp) CBlock block; - if (!block.ReadFromDisk(pblkindex, true)) + if (!ReadBlockFromDisk(block, pblkindex, Params().GetConsensus())) throw JSONRPCError(RPC_PARSE_ERROR, "Unable to read block from disk!"); for (unsigned int i = 1; i < block.vtx.size(); i++) @@ -1284,7 +1286,7 @@ UniValue scanforunspent(const UniValue& params, bool fHelp) CBlock block; - if (!block.ReadFromDisk(pblkindex, true)) + if (!ReadBlockFromDisk(block, pblkindex, Params().GetConsensus())) throw JSONRPCError(RPC_PARSE_ERROR, "Unable to read block from disk!"); for (unsigned int i = 1; i < block.vtx.size(); i++) diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp index 02ecb53830..1bea7febf0 100644 --- a/src/txdb-leveldb.cpp +++ b/src/txdb-leveldb.cpp @@ -12,9 +12,11 @@ #include #include +#include "chainparams.h" #include "gridcoin/staking/kernel.h" #include "txdb.h" #include "main.h" +#include "node/blockstorage.h" #include "ui_interface.h" #include "util.h" #include "validation.h" @@ -458,7 +460,7 @@ bool CTxDB::LoadBlockIndex() if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) return error("LoadBlockIndex() : block.ReadFromDisk failed"); // check level 1: verify block validity // check level 7: verify block signature too @@ -580,7 +582,7 @@ bool CTxDB::LoadBlockIndex() // Reorg back to the fork LogPrintf("LoadBlockIndex() : *** moving best chain pointer back to block %d", pindexFork->nHeight); CBlock block; - if (!block.ReadFromDisk(pindexFork)) + if (!ReadBlockFromDisk(block, pindexFork, Params().GetConsensus())) return error("LoadBlockIndex() : block.ReadFromDisk failed"); CTxDB txdb; SetBestChain(txdb, block, pindexFork); diff --git a/src/validation.cpp b/src/validation.cpp index fc1cb1d3b6..d03056115e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -6,6 +6,7 @@ #include "checkpoints.h" #include "main.h" #include "gridcoin/staking/kernel.h" +#include "node/blockstorage.h" #include "policy/fees.h" #include "serialize.h" #include "txdb-leveldb.h" @@ -510,7 +511,7 @@ int GetDepthInMainChain(const CTxIndex& txi) { // Read block header CBlock block; - if (!block.ReadFromDisk(txi.pos.nFile, txi.pos.nBlockPos, false)) + if (!ReadBlockFromDisk(block, txi.pos.nFile, txi.pos.nBlockPos, Params().GetConsensus(), false)) return 0; // Find the block in the index BlockMap::iterator mi = mapBlockIndex.find(block.GetHash(true)); @@ -521,3 +522,24 @@ int GetDepthInMainChain(const CTxIndex& txi) return 0; return 1 + nBestHeight - pindex->nHeight; } + + +bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params) +{ + bool fNegative; + bool fOverflow; + arith_uint256 bnTarget; + + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); + + // Check range + if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit)) + return error("%s: nBits below minimum work", __func__); + + // Check proof of work matches claimed amount + if (UintToArith256(hash) > bnTarget) + return error("%s: hash doesn't match nBits", __func__); + + return true; +} + diff --git a/src/validation.h b/src/validation.h index 3b3dc6c5c2..e9add488d6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -14,6 +14,9 @@ class CTxDB; class CBlockHeader; +namespace Consensus { + struct Params; +} typedef std::map> MapPrevTx; @@ -92,4 +95,6 @@ bool GetCoinAge(const CTransaction& tx, CTxDB& txdb, uint64_t& nCoinAge); // ppc int GetDepthInMainChain(const CTxIndex& txi); +bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params); + #endif // BITCOIN_VALIDATION_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ddc84e9e80..52ceb30164 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "txdb.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" @@ -23,6 +24,7 @@ #include "gridcoin/staking/kernel.h" #include "gridcoin/support/block_finder.h" #include "policy/fees.h" +#include "node/blockstorage.h" using namespace std; @@ -1020,7 +1022,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) } CBlock block; - block.ReadFromDisk(pindex, true); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); for (auto const& tx : block.vtx) { if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))