diff --git a/src/Makefile.am b/src/Makefile.am index 1797f237..e3eeddb3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,7 @@ BITCOIN_CORE_H = \ addrman.h \ alert.h \ allocators.h \ + auxpow.h \ base58.h bignum.h \ bloom.h \ chainparams.h \ @@ -108,6 +109,7 @@ version.o: obj/build.h libbitcoin_server_a_SOURCES = \ addrman.cpp \ alert.cpp \ + auxpow.cpp \ bloom.cpp \ checkpoints.cpp \ coins.cpp \ diff --git a/src/core.h b/src/core.h index 1419880d..9d0fdd73 100644 --- a/src/core.h +++ b/src/core.h @@ -85,6 +85,16 @@ inline std::string GetAlgoName(int Algo) } class CTransaction; +class CAuxPow; + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionSerialize ser_action); + +template +int ReadWriteAuxPow(Stream& s, boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionUnserialize ser_action); + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionGetSerializeSize ser_action); /** No amount larger than this (in satoshi) is valid */ static const int64_t MAX_MONEY = 2000000000 * COIN; @@ -413,17 +423,6 @@ public: ) }; -class CAuxPow; - -template -int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionSerialize ser_action); - -template -int ReadWriteAuxPow(Stream& s, boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionUnserialize ser_action); - -template -int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionGetSerializeSize ser_action); - /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block diff --git a/src/main.cpp b/src/main.cpp index 718f84c6..75a4c3b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1135,17 +1135,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -std::string CDiskBlockIndex::ToString() const -{ - std::string str = "CDiskBlockIndex("; - str += CBlockIndex::ToString(); - str += strprintf("\n hashBlock=%s, hashPrev=%s, hashParentBlock=%s)", - GetBlockHash().ToString().c_str(), - hashPrev.ToString().c_str(), - (auxpow.get() != NULL) ? auxpow->GetParentBlockHash(GetAlgo()).ToString().substr(0,20).c_str() : "-"); - return str; -} - bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) { // Open history file to append @@ -1190,7 +1179,8 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) } // Check the header - if (!CheckProofOfWork(block.GetPoWHash(block.GetAlgo()), block.nBits, block.GetAlgo())) + //if (!CheckProofOfWork(block.GetPoWHash(block.GetAlgo()), block.nBits, block.GetAlgo())) + if (!block.CheckProofOfWork(mapBlockIndex[block.GetHash()]->nHeight)) return error("ReadBlockFromDisk : Errors in block header"); return true; @@ -1533,7 +1523,7 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits, int algo) bool CBlockHeader::CheckProofOfWork(int nHeight) const { int algo = GetAlgo(); - if ((nHeight >= GetAuxPowStartBlock()) && (algo == ALGO_SHA256D && algo == ALGO_SCRYPT)) + if ((nHeight >= GetAuxPowStartBlock()) && (algo == ALGO_SHA256D || algo == ALGO_SCRYPT)) { // Prevent same work from being submitted twice: // - this block must have our chain ID @@ -1999,7 +1989,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C { AssertLockHeld(cs_main); // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + //if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, pindex->nHeight, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -2409,27 +2400,6 @@ bool ActivateBestChain(CValidationState &state) { return true; } -CBlockHeader CBlockIndex::GetBlockHeader() const -{ - CBlockHeader block; - - if (nVersion & BLOCK_VERSION_AUXPOW) { - CDiskBlockIndex diskblockindex; - // auxpow is not in memory, load CDiskBlockHeader - // from database to get it - pblocktree->ReadDiskBlockIndex(*phashBlock, diskblockindex); - block.auxpow = diskblockindex.auxpow; - } - block.nVersion = nVersion; - if (pprev) - block.hashPrevBlock = pprev->GetBlockHash(); - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block; -} - bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) { // Check for duplicate @@ -2466,10 +2436,8 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; setBlockIndexValid.insert(pindexNew); - //if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) - if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew)) /* write both the immutible data (CDiskBlockIndex) and the mutable data (BlockIndex) */ - //if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew)) + if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew)) return state.Abort(_("Failed to write block index")); // New best? @@ -2590,26 +2558,66 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, int nHeight, bool fCheckPOW) +{ + // Check proof of work matches claimed amount + if (fCheckPOW && !block.CheckProofOfWork(nHeight)) + //if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(block.GetAlgo()), block.nBits, block.GetAlgo())) + return state.DoS(50, error("CheckBlockHeader() : proof of work failed"), + REJECT_INVALID, "high-hash"); -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) + // Check timestamp + if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + return state.Invalid(error("CheckBlockHeader() : block timestamp too far in the future"), + REJECT_INVALID, "time-too-new"); + + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64_t deltaTime = block.GetBlockTime() - pcheckpoint->nTime; + if (deltaTime < 0) + { + LogPrintf("CheckBlockHeader(): Height=%d, hash=%s, BlockTime=%d, Checkpoint=%d\n", nHeight, block.GetHash().GetHex().c_str(), block.GetBlockTime(), pcheckpoint->nTime); + return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"), + REJECT_CHECKPOINT, "time-too-old"); + } + CBigNum bnNewBlock; + bnNewBlock.SetCompact(block.nBits); + CBigNum bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (bnNewBlock > bnRequired) + { + return state.DoS(100, error("CheckBlockHeader() : block with too little proof-of-work"), + REJECT_INVALID, "bad-diffbits"); + } + } + + return true; +} + +bool CheckBlock(const CBlock& block, CValidationState& state, int nHeight, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context // that can be verified before saving an orphan block. + if (!CheckBlockHeader(block, state, nHeight, fCheckPOW)) + return false; + // 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"); // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(block.GetAlgo()), block.nBits, block.GetAlgo())) - return state.DoS(50, error("CheckBlock() : proof of work failed"), - REJECT_INVALID, "high-hash"); + // if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(block.GetAlgo()), block.nBits, block.GetAlgo())) + // return state.DoS(50, error("CheckBlock() : proof of work failed"), + // REJECT_INVALID, "high-hash"); // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), - REJECT_INVALID, "time-too-new"); + // if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + // return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), + // REJECT_INVALID, "time-too-new"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) @@ -2812,6 +2820,48 @@ int64_t CBlockIndex::GetMedianTime() const return pindex->GetMedianTimePast(); } +std::string CBlockIndex::ToString() const +{ + return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, nHeight, + hashMerkleRoot.ToString().substr(0,10).c_str(), + GetBlockHash().ToString().c_str()); +} + +std::string CDiskBlockIndex::ToString() const +{ + std::string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s, hashParentBlock=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().c_str(), + (auxpow.get() != NULL) ? auxpow->GetParentBlockHash(GetAlgo()).ToString().substr(0,20).c_str() : "-"); + return str; +} + +CBlockHeader CBlockIndex::GetBlockHeader() const +{ + CBlockHeader block; + + if (nVersion & BLOCK_VERSION_AUXPOW) { + CDiskBlockIndex diskblockindex; + // auxpow is not in memory, load CDiskBlockHeader + // from database to get it + + pblocktree->ReadDiskBlockIndex(*phashBlock, diskblockindex); + block.auxpow = diskblockindex.auxpow; + } + + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; +} + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) { AssertLockHeld(cs_main); @@ -2835,9 +2885,12 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl if (mapOrphanBlocks.count(hash)) return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate"); - // Preliminary checks - if (!CheckBlock(*pblock, state)) + if (!CheckBlock(*pblock, state, INT_MAX)) + { + if (state.CorruptionPossible()) + mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); return error("ProcessBlock() : CheckBlock FAILED"); + } CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) @@ -3235,7 +3288,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(block, state)) + if (nCheckLevel >= 1 && !CheckBlock(block, state, pindex->nHeight)) 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) { diff --git a/src/main.h b/src/main.h index ae5bef7b..ef1ca14f 100644 --- a/src/main.h +++ b/src/main.h @@ -622,7 +622,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos); // Context-independent validity checks -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, int nHeight, bool fCheckPOW = true); +bool CheckBlock(const CBlock& block, CValidationState& state, int nHeight, bool fCheckPOW = true, bool fCheckMerkleRoot = true); // Store block on disk // if dbp is provided, the file is known to already reside on disk @@ -994,10 +995,12 @@ public: bool CheckIndex() const { - int algo = GetAlgo(); + /* This check was considered unneccessary given the other safeguards like the genesis and checkpoints, and it breaks LoadBlockIndexGuts() after AuxPow is enabled. */ + + /* int algo = GetAlgo(); if (algo == ALGO_SHA256D) return CheckProofOfWork(GetBlockHash(), nBits, algo); - else + else*/ return true; } @@ -1026,13 +1029,7 @@ public: static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck); - std::string ToString() const - { - return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, nHeight, - hashMerkleRoot.ToString().c_str(), - GetBlockHash().ToString().c_str()); - } + std::string ToString() const; //moved code to main.cpp because new method required access to auxpow void print() const { @@ -1096,7 +1093,7 @@ public: } - std::string ToString() const; + std::string ToString() const; // moved code to main.cpp void print() const { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 264dbec2..009cd5ff 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -16,6 +16,9 @@ using namespace json_spirit; using namespace std; +// from rpcraw.cpp +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry); + void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex); double GetDifficulty(const CBlockIndex* blockindex, int algo) @@ -76,6 +79,38 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) result.push_back(Pair("pow_algo", GetAlgoName(algo))); result.push_back(Pair("pow_hash", block.GetPoWHash(algo).GetHex())); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + if (block.nVersion & BLOCK_VERSION_AUXPOW) { + // this block includes auxpow + Object auxpow; + auxpow.push_back(Pair("size", (int)::GetSerializeSize(*block.auxpow, SER_NETWORK, PROTOCOL_VERSION))); + + Object coinbasetx; + TxToJSON(*block.auxpow, 0, coinbasetx); + auxpow.push_back(Pair("coinbasetx", Value(coinbasetx))); + + Array coinbaseMerkle; + BOOST_FOREACH(const uint256 &hash, block.auxpow->vMerkleBranch) + coinbaseMerkle.push_back(hash.GetHex()); + auxpow.push_back(Pair("coinbaseMerkleBranch", coinbaseMerkle)); + auxpow.push_back(Pair("coinbaseIndex", block.auxpow->nIndex)); + + Array chainMerkle; + BOOST_FOREACH(const uint256 &hash, block.auxpow->vChainMerkleBranch) + chainMerkle.push_back(hash.GetHex()); + auxpow.push_back(Pair("chainMerkleBranch", chainMerkle)); + auxpow.push_back(Pair("chainIndex", (boost::uint64_t)block.auxpow->nChainIndex)); + + Object parent_block; + parent_block.push_back(Pair("hash", block.auxpow->parentBlockHeader.GetHash().GetHex())); + parent_block.push_back(Pair("version", (boost::uint64_t)block.auxpow->parentBlockHeader.nVersion)); + parent_block.push_back(Pair("previousblockhash", block.auxpow->parentBlockHeader.hashPrevBlock.GetHex())); + parent_block.push_back(Pair("merkleroot", block.auxpow->parentBlockHeader.hashMerkleRoot.GetHex())); + parent_block.push_back(Pair("time", (boost::int64_t)block.auxpow->parentBlockHeader.nTime)); + parent_block.push_back(Pair("bits", HexBits(block.auxpow->parentBlockHeader.nBits))); + parent_block.push_back(Pair("nonce", (boost::uint64_t)block.auxpow->parentBlockHeader.nNonce)); + auxpow.push_back(Pair("parent_block", Value(parent_block))); + result.push_back(Pair("auxpow", Value(auxpow))); + } Array txs; BOOST_FOREACH(const CTransaction&tx, block.vtx) txs.push_back(tx.GetHash().GetHex()); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index d8e87cf2..c34fd74a 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -315,7 +315,7 @@ static const CRPCCommand vRPCCommands[] = { "gethashespersec", &gethashespersec, true, false, false }, { "getwork", &getwork, true, false, true }, { "setgenerate", &setgenerate, true, true, false }, - + /* Stealth */ { "getnewstealthaddress", &getnewstealthaddress, false, false, true}, { "liststealthaddresses", &liststealthaddresses, false, false, true}, diff --git a/src/txdb.cpp b/src/txdb.cpp index b155b3ad..dc3ffeb0 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -215,35 +215,35 @@ bool CBlockTreeDB::LoadBlockIndexGuts() char chType; ssKey >> chType; if (chType == 'b') { + // Detect pre-auxpow keys in the database and abort if found + if (slKey.size() < ssKeySet.size()) { + return error("Database key size is %d expected %d, require reindex to upgrade.", slKey.size(), ssKeySet.size()); + } + ssKey >> hash; leveldb::Slice slValue = pcursor->value(); - // CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CDataStream ssValue_immutable(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CDiskBlockIndex diskindex; - // ssValue >> diskindex; ssValue_immutable >> diskindex; // read all immutable data // Construct immutable parts of block index objecty CBlockIndex* pindexNew = InsertBlockIndex(hash); assert(diskindex.GetBlockHash() == *pindexNew->phashBlock); // paranoia check - // // Construct block index object - // CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); pindexNew->nHeight = diskindex.nHeight; - // pindexNew->nFile = diskindex.nFile; - // pindexNew->nDataPos = diskindex.nDataPos; - // pindexNew->nUndoPos = diskindex.nUndoPos; + pindexNew->nFile = diskindex.nFile; + pindexNew->nDataPos = diskindex.nDataPos; + pindexNew->nUndoPos = diskindex.nUndoPos; pindexNew->nVersion = diskindex.nVersion; pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; - // pindexNew->nStatus = diskindex.nStatus; + pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - // if (!pindexNew->CheckIndex()) // CheckIndex need phashBlock to be set diskindex.phashBlock = pindexNew->phashBlock; if (!diskindex.CheckIndex())