From 0e11c91be3b6186c1dab1c3312ab40bbd8cf96be Mon Sep 17 00:00:00 2001 From: sidhujag Date: Thu, 22 Dec 2016 12:44:45 -0800 Subject: [PATCH] six auxpow --- src/auxpow.cpp | 40 +++++++++++++++++++++++ src/auxpow.h | 16 ++++++++-- src/chain.h | 12 ------- src/main.cpp | 66 ++++++++++++++++++++++++++++++-------- src/main.h | 15 +++++++++ src/rpc/mining.cpp | 79 +++++++++++++++++----------------------------- src/txdb.cpp | 11 ------- src/version.h | 6 +++- 8 files changed, 154 insertions(+), 91 deletions(-) diff --git a/src/auxpow.cpp b/src/auxpow.cpp index 10da024ab78c9a..a7e4dee8e0cb41 100644 --- a/src/auxpow.cpp +++ b/src/auxpow.cpp @@ -34,11 +34,15 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) break; if (nIndex == (int)block.vtx.size()) { + vMerkleBranch.clear(); nIndex = -1; LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); return 0; } + // Fill in merkle branch + vMerkleBranch = BlockMerkleBranch (block, nIndex); + // Is the tx in a block that's in the main chain BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) @@ -206,4 +210,40 @@ CAuxPow::CheckMerkleBranch (uint256 hash, nIndex >>= 1; } return hash; +} +void +CAuxPow::initAuxPow (CBlockHeader& header) +{ + /* Set auxpow flag right now, since we take the block hash below. */ + header.SetAuxpowVersion(true); + + /* Build a minimal coinbase script input for merge-mining. */ + const uint256 blockHash = header.GetHash (); + std::vector inputData(blockHash.begin (), blockHash.end ()); + std::reverse (inputData.begin (), inputData.end ()); + inputData.push_back (1); + inputData.insert (inputData.end (), 7, 0); + + /* Fake a parent-block coinbase with just the required input + script and no outputs. */ + CMutableTransaction coinbase; + coinbase.vin.resize (1); + coinbase.vin[0].prevout.SetNull (); + coinbase.vin[0].scriptSig = (CScript () << inputData); + assert (coinbase.vout.empty ()); + + /* Build a fake parent block with the coinbase. */ + CBlock parent; + parent.nVersion = 1; + parent.vtx.resize (1); + parent.vtx[0] = coinbase; + parent.hashMerkleRoot = BlockMerkleRoot (parent); + + /* Construct the auxpow object. */ + header.SetAuxpow (new CAuxPow (coinbase)); + assert (header.auxpow->vChainMerkleBranch.empty ()); + header.auxpow->nChainIndex = 0; + assert (header.auxpow->vMerkleBranch.empty ()); + header.auxpow->nIndex = 0; + header.auxpow->parentBlock = parent; } \ No newline at end of file diff --git a/src/auxpow.h b/src/auxpow.h index dd2885b9e6b830..1c6874e1de803a 100644 --- a/src/auxpow.h +++ b/src/auxpow.h @@ -16,8 +16,10 @@ #include class CBlock; +class CBlockHeader; class CBlockIndex; class CValidationState; + /** Header for merge-mining data in the coinbase. */ static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' }; @@ -33,7 +35,8 @@ class CMerkleTx : public CTransaction public: uint256 hashBlock; - std::vector vMerkleBranch; + std::vector vMerkleBranch; + /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest * block in the chain we know this or any in-wallet dependency conflicts * with. Older clients interpret nIndex == -1 as unconfirmed for backward @@ -61,8 +64,6 @@ class CMerkleTx : public CTransaction template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - // SYSCOIN - //std::vector vMerkleBranch; // For compatibility with older versions. READWRITE(*(CTransaction*)this); nVersion = this->nVersion; READWRITE(hashBlock); @@ -188,6 +189,15 @@ class CAuxPow : public CMerkleTx const std::vector& vMerkleBranch, int nIndex); + /** + * Initialise the auxpow of the given block header. This constructs + * a minimal CAuxPow object with a minimal parent block and sets + * it on the block header. The auxpow is not necessarily valid, but + * can be "mined" to make it valid. + * @param header The header to set the auxpow on. + */ + static void initAuxPow (CBlockHeader& header); + }; #endif // SYSCOIN_AUXPOW_H \ No newline at end of file diff --git a/src/chain.h b/src/chain.h index fc9c4ee6fdc6a5..110e7eb6f897d3 100644 --- a/src/chain.h +++ b/src/chain.h @@ -165,8 +165,6 @@ class CBlockIndex //! pointer to the index of some further predecessor of this block CBlockIndex* pskip; - // SYSCOIN pointer to the AuxPoW header, if this block has one - boost::shared_ptr pauxpow; //! height of the entry in the chain. The genesis block has height 0 int nHeight; @@ -211,8 +209,6 @@ class CBlockIndex phashBlock = NULL; pprev = NULL; pskip = NULL; - // SYSCOIN - pauxpow.reset(); nHeight = 0; nFile = 0; nDataPos = 0; @@ -374,14 +370,6 @@ class CDiskBlockIndex : public CBlockIndex READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); - // SYSCOIN - if (this->nVersion.IsAuxpow()) { - if (ser_action.ForRead()) - pauxpow.reset(new CAuxPow()); - assert(pauxpow); - READWRITE(*pauxpow); - } else if (ser_action.ForRead()) - pauxpow.reset(); } uint256 GetBlockHash() const diff --git a/src/main.cpp b/src/main.cpp index 82a17332cdfcf4..758fa62859f383 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3643,11 +3643,6 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->BuildSkip(); } pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); - // SYSCOIN: Add AuxPoW - if (block.nVersion.IsAuxpow()) { - pindexNew->pauxpow = block.auxpow; - assert(NULL != pindexNew->pauxpow.get()); - } pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) pindexBestHeader = pindexNew; @@ -5876,20 +5871,40 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector vHeaders; int nLimit = MAX_HEADERS_RESULTS; + // SYSCOIN + unsigned nSize = 0; LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { // SYSCOIN - vHeaders.push_back(pindex->GetBlockHeader(chainparams.GetConsensus())); + const CBlockHeader &header = pindex->GetBlockHeader(chainparams.GetConsensus()); + vHeaders.push_back(header); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) + break; + // SYSCOIN + nSize += GetSerializeSize(header, SER_NETWORK, PROTOCOL_VERSION); + if (pfrom->nVersion >= SIZE_HEADERS_LIMIT_VERSION + && nSize >= THRESHOLD_HEADERS_SIZE) break; } - // pindex can be NULL either if we sent chainActive.Tip() OR - // if our peer has chainActive.Tip() (and thus we are sending an empty - // headers message). In both cases it's safe to update - // pindexBestHeaderSent to be our tip. - nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); - pfrom->PushMessage(NetMsgType::HEADERS, vHeaders); + // SYSCOIN + /* Check maximum headers size before pushing the message + if the peer enforces it. This should not fail since we + break above in the loop at the threshold and the threshold + should be small enough in comparison to the hard max size. + Do it nevertheless to be sure. */ + if (pfrom->nVersion >= SIZE_HEADERS_LIMIT_VERSION + && nSize > MAX_HEADERS_SIZE) + LogPrintf("ERROR: not pushing 'headers', too large\n"); + else + { + // pindex can be NULL either if we sent chainActive.Tip() OR + // if our peer has chainActive.Tip() (and thus we are sending an empty + // headers message). In both cases it's safe to update + // pindexBestHeaderSent to be our tip. + nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); + pfrom->PushMessage(NetMsgType::HEADERS, vHeaders); + } } @@ -6320,9 +6335,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return error("headers message size = %u", nCount); } headers.resize(nCount); + // SYSCOIN + unsigned nSize = 0; for (unsigned int n = 0; n < nCount; n++) { vRecv >> headers[n]; ReadCompactSize(vRecv); // ignore tx count; assume it is 0. + //SYSCOIN + nSize += GetSerializeSize(headers[n], SER_NETWORK, PROTOCOL_VERSION); + if (pfrom->nVersion >= SIZE_HEADERS_LIMIT_VERSION + && nSize > MAX_HEADERS_SIZE) { + Misbehaving(pfrom->GetId(), 20); + return error("headers message size = %u", nSize); + } } { @@ -6386,8 +6410,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, assert(pindexLast); UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - - if (nCount == MAX_HEADERS_RESULTS) { + // SYSCOIN + // If we already know the last header in the message, then it contains + // no new information for us. In this case, we do not request + // more headers later. This prevents multiple chains of redundant + // getheader requests from running in parallel if triggered by incoming + // blocks while the node is still in initial headers sync. + const bool hasNewHeaders = (mapBlockIndex.count(headers.back().GetHash()) == 0); + + bool maxSize = (nCount == MAX_HEADERS_RESULTS); + if (pfrom->nVersion >= SIZE_HEADERS_LIMIT_VERSION + && nSize >= THRESHOLD_HEADERS_SIZE) + maxSize = true; + // FIXME: This change (with hasNewHeaders) is rolled back in Bitcoin, + // but I think it should stay here for merge-mined coins. Try to get + // it fixed again upstream and then update the fix. + if (maxSize && hasNewHeaders) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. diff --git a/src/main.h b/src/main.h index 687c4a140f4be5..5f0c107e386137 100644 --- a/src/main.h +++ b/src/main.h @@ -89,6 +89,21 @@ static const unsigned int BLOCK_STALLING_TIMEOUT = 2; /** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends * less than this number, we reached its tip. Changing this value is a protocol upgrade. */ static const unsigned int MAX_HEADERS_RESULTS = 2000; +// SYSCOIN +/** Maximum size of a "headers" message. This is enforced starting with + * SIZE_HEADERS_LIMIT_VERSION peers and prevents overloading if we have + * very large headers (due to auxpow). + */ +static const unsigned int MAX_HEADERS_SIZE = (6 << 20); // 6 MiB +/** Size of a headers message that is the threshold for assuming that the + * peer has more headers (even if we have less than MAX_HEADERS_RESULTS). + * This is used starting with SIZE_HEADERS_LIMIT_VERSION peers. + */ +static const unsigned int THRESHOLD_HEADERS_SIZE = (4 << 20); // 4 MiB +/** Size of the "block download window": how far ahead of our current height do we fetch? + * Larger windows tolerate larger download speed differences between peer, but increase the potential + * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning + * harder). We'll probably want to make this a per-peer adaptive value at some point. */ /** Maximum depth of blocks we're willing to serve as compact blocks to peers * when requested. For older blocks, a regular BLOCK response will be sent. */ static const int MAX_CMPCTBLOCK_DEPTH = 5; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index fe6aabcf7929ff..7e3dced4101685 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -120,6 +120,9 @@ UniValue generateBlocks(boost::shared_ptr coinbaseScript, int nG LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } + // SYSCOIN + CAuxPow::initAuxPow(*pblock); + const CPureBlockHeader& miningHeader = pblock->auxpow->parentBlock; while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { ++pblock->nNonce; --nMaxTries; @@ -127,7 +130,8 @@ UniValue generateBlocks(boost::shared_ptr coinbaseScript, int nG if (nMaxTries == 0) { break; } - if (pblock->nNonce == nInnerLoopCount) { + // SYSCOIN + if (miningHeader.nNonce == nInnerLoopCount) { continue; } CValidationState state; @@ -938,7 +942,7 @@ UniValue getauxblock(const UniValue& params, bool fHelp) " \"coinbasevalue\" (numeric) value of the block's coinbase\n" " \"bits\" (string) compressed target of the block\n" " \"height\" (numeric) height of the block\n" - " \"target\" (string) target in reversed byte order, deprecated\n" + " \"_target\" (string) target in reversed byte order, deprecated\n" "}\n" "\nResult (with arguments):\n" "xxxxx (boolean) whether the submitted block was correct\n" @@ -966,6 +970,7 @@ UniValue getauxblock(const UniValue& params, bool fHelp) if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Syscoin is downloading blocks..."); + /* The variables below are used to keep track of created and not yet submitted auxpow blocks. Lock them to be sure even for multiple @@ -973,15 +978,15 @@ UniValue getauxblock(const UniValue& params, bool fHelp) static CCriticalSection cs_auxblockCache; LOCK(cs_auxblockCache); static std::map mapNewBlock; - static std::vector vNewBlockTemplate; + static std::vector> vNewBlockTemplate; /* Create a new block? */ if (params.size() == 0) { static unsigned nTransactionsUpdatedLast; - static const CBlockIndex* pindexPrev = NULL; + static const CBlockIndex* pindexPrev = nullptr; static uint64_t nStart; - static CBlockTemplate* pblocktemplate; + static CBlock* pblock = nullptr; static unsigned nExtraNonce = 0; // Update block @@ -993,16 +998,15 @@ UniValue getauxblock(const UniValue& params, bool fHelp) { if (pindexPrev != chainActive.Tip()) { - // Deallocate old blocks since they're obsolete now + // Clear old blocks since they're obsolete now. mapNewBlock.clear(); - BOOST_FOREACH(CBlockTemplate* pbt, vNewBlockTemplate) - delete pbt; vNewBlockTemplate.clear(); + pblock = nullptr; } // Create new block with nonce = 0 and extraNonce = 1 - pblocktemplate = BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript); - if (!pblocktemplate) + std::unique_ptr newBlock(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); + if (!newBlock) throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory"); // Update state only when CreateNewBlock succeeded @@ -1011,32 +1015,30 @@ UniValue getauxblock(const UniValue& params, bool fHelp) nStart = GetTime(); // Finalise it by setting the version and building the merkle root - CBlock* pblock = &pblocktemplate->block; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - pblock->nVersion.SetAuxpow(true); + IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce); + newBlock->block.nVersion.SetAuxpow(true); // Save + pblock = &newBlock->block; mapNewBlock[pblock->GetHash()] = pblock; - vNewBlockTemplate.push_back(pblocktemplate); + vNewBlockTemplate.push_back(std::move(newBlock)); } } - const CBlock& block = pblocktemplate->block; - arith_uint256 target; bool fNegative, fOverflow; - target.SetCompact(block.nBits, &fNegative, &fOverflow); + target.SetCompact(pblock->nBits, &fNegative, &fOverflow); if (fNegative || fOverflow || target == 0) throw std::runtime_error("invalid difficulty bits in block"); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", block.GetHash().GetHex())); - result.push_back(Pair("chainid", block.nVersion.GetChainId())); - result.push_back(Pair("previousblockhash", block.hashPrevBlock.GetHex())); - result.push_back(Pair("coinbasevalue", (int64_t)block.vtx[0].vout[0].nValue)); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); + result.push_back(Pair("hash", pblock->GetHash().GetHex())); + result.push_back(Pair("chainid", pblock->GetChainId())); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("height", static_cast (pindexPrev->nHeight + 1))); - result.push_back(Pair("target", HexStr(BEGIN(target), END(target)))); + result.push_back(Pair("_target", HexStr(BEGIN(target), END(target)))); return result; } @@ -1060,39 +1062,16 @@ UniValue getauxblock(const UniValue& params, bool fHelp) block.SetAuxpow(new CAuxPow(pow)); assert(block.GetHash() == hash); - // This is a straight cut & paste job from submitblock() - bool fBlockPresent = false; - { - LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; - if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) - return "duplicate"; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return "duplicate-invalid"; - // Otherwise, we might only have the header - process the block before returning - fBlockPresent = true; - } - } - CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, false); + bool fAccepted = ProcessNewBlock(state, Params(), nullptr, &block, + true, nullptr, false); UnregisterValidationInterface(&sc); - if (fBlockPresent) - { - if (fAccepted && !sc.found) - return "duplicate-inconclusive"; - return "duplicate"; - } + if (fAccepted) - { - if (!sc.found) - return "inconclusive"; coinbaseScript->KeepScript(); - } + return fAccepted; } static const CRPCCommand commands[] = diff --git a/src/txdb.cpp b/src/txdb.cpp index 4ac495a4c36998..59a7bf3098cc9a 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -198,17 +198,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts(boost::functionnNonce = diskindex.nNonce; pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - - // SYSCOIN disk auxpow check - if (pindexNew->nVersion.IsAuxpow()) { - if (!diskindex.pauxpow->check(diskindex.GetBlockHash(), pindexNew->nVersion.GetChainId(), Params().GetConsensus())) { - return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); - } - } else { - if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) - return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); - } - pcursor->Next(); } else { return error("LoadBlockIndex() : failed to read value"); diff --git a/src/version.h b/src/version.h index 5909078439963a..661a2250bfdc7e 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ * network protocol versioning */ // SYSCOIN -static const int PROTOCOL_VERSION = 70028; +static const int PROTOCOL_VERSION = 70029; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -37,6 +37,10 @@ static const int NO_BLOOM_VERSION = 70011; //! "sendheaders" command and announcing blocks with headers starts with this version static const int SENDHEADERS_VERSION = 70012; +//! Version when we switched to a size-based "headers" limit. +// SYSCOIN +static const int SIZE_HEADERS_LIMIT_VERSION = 70029; + //! "feefilter" tells peers to filter invs to you by fee starts with this version static const int FEEFILTER_VERSION = 70013;