diff options
Diffstat (limited to 'src/rpc/mining.cpp')
| -rw-r--r-- | src/rpc/mining.cpp | 267 |
1 files changed, 264 insertions, 3 deletions
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 33e234a95..70192686b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -121,14 +121,16 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } - while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { - ++pblock->nNonce; + CAuxPow::initAuxPow(*pblock); + CPureBlockHeader& miningHeader = pblock->auxpow->parentBlock; + while (nMaxTries > 0 && miningHeader.nNonce < nInnerLoopCount && !CheckProofOfWork(miningHeader.GetHash(), pblock->nBits, Params().GetConsensus())) { + ++miningHeader.nNonce; --nMaxTries; } if (nMaxTries == 0) { break; } - if (pblock->nNonce == nInnerLoopCount) { + if (miningHeader.nNonce == nInnerLoopCount) { continue; } std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock); @@ -925,6 +927,262 @@ UniValue estimatesmartpriority(const JSONRPCRequest& request) return result; } +/* ************************************************************************** */ +/* Merge mining. */ + +/** + * The variables below are used to keep track of created and not yet + * submitted auxpow blocks. Lock them to be sure even for multiple + * RPC threads running in parallel. + */ +static CCriticalSection cs_auxblockCache; +static std::map<uint256, CBlock*> mapNewBlock; +static std::vector<std::unique_ptr<CBlockTemplate>> vNewBlockTemplate; + +static +void AuxMiningCheck() +{ + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, + "Error: Peer-to-peer functionality missing or disabled"); + + if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 + && !Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, + "Dogecoin is not connected!"); + + if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, + "Dogecoin is downloading blocks..."); + + /* This should never fail, since the chain is already + past the point of merge-mining start. Check nevertheless. */ + { + LOCK(cs_main); + if (chainActive.Height() + 1 < Params().GetConsensus().nAuxpowStartHeight) + throw std::runtime_error("mining auxblock method is not yet available"); + } +} + +static +UniValue AuxMiningCreateBlock(const CScript& scriptPubKey) +{ + AuxMiningCheck(); + + LOCK(cs_auxblockCache); + + static unsigned nTransactionsUpdatedLast; + static const CBlockIndex* pindexPrev = nullptr; + static uint64_t nStart; + static CBlock* pblock = nullptr; + static unsigned nExtraNonce = 0; + + // Update block + { + LOCK(cs_main); + if (pindexPrev != chainActive.Tip() + || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast + && GetTime() - nStart > 60)) + { + if (pindexPrev != chainActive.Tip()) + { + // Clear old blocks since they're obsolete now. + mapNewBlock.clear(); + vNewBlockTemplate.clear(); + pblock = nullptr; + } + + // Create new block with nonce = 0 and extraNonce = 1 + std::unique_ptr<CBlockTemplate> newBlock + = BlockAssembler(Params()).CreateNewBlock(scriptPubKey); + if (!newBlock) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory"); + + // Update state only when CreateNewBlock succeeded + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + pindexPrev = chainActive.Tip(); + nStart = GetTime(); + + // Finalise it by setting the version and building the merkle root + IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce); + newBlock->block.SetAuxpowFlag(true); + + // Save + pblock = &newBlock->block; + mapNewBlock[pblock->GetHash()] = pblock; + vNewBlockTemplate.push_back(std::move(newBlock)); + } + } + + // At this point, pblock is always initialised: If we make it here + // without creating a new block above, it means that, in particular, + // pindexPrev == chainActive.Tip(). But for that to happen, we must + // already have created a pblock in a previous call, as pindexPrev is + // initialised only when pblock is. + assert(pblock); + + arith_uint256 target; + bool 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.pushKV("hash", pblock->GetHash().GetHex()); + result.pushKV("chainid", pblock->GetChainId()); + result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); + result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue); + result.pushKV("bits", strprintf("%08x", pblock->nBits)); + result.pushKV("height", static_cast<int64_t> (pindexPrev->nHeight + 1)); + result.pushKV("_target", HexStr(BEGIN(target), END(target))); + + return result; +} + +static +bool AuxMiningSubmitBlock(const std::string& hashHex, + const std::string& auxpowHex) +{ + AuxMiningCheck(); + + LOCK(cs_auxblockCache); + + uint256 hash; + hash.SetHex(hashHex); + + const std::map<uint256, CBlock*>::iterator mit = mapNewBlock.find(hash); + if (mit == mapNewBlock.end()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown"); + CBlock& block = *mit->second; + + const std::vector<unsigned char> vchAuxPow = ParseHex(auxpowHex); + CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); + CAuxPow pow; + ss >> pow; + block.SetAuxpow(new CAuxPow(pow)); + assert(block.GetHash() == hash); + + submitblock_StateCatcher sc(block.GetHash()); + RegisterValidationInterface(&sc); + std::shared_ptr<const CBlock> shared_block + = std::make_shared<const CBlock>(block); + bool fAccepted = ProcessNewBlock(Params(), shared_block, true, nullptr); + UnregisterValidationInterface(&sc); + + return fAccepted; +} + +UniValue getauxblock(const JSONRPCRequest& request) +{ + if (request.fHelp + || (request.params.size() != 0 && request.params.size() != 2)) + throw std::runtime_error( + "getauxblock (hash auxpow)\n" + "\nCreate or submit a merge-mined block.\n" + "\nWithout arguments, create a new block and return information\n" + "required to merge-mine it. With arguments, submit a solved\n" + "auxpow for a previously returned block.\n" + "\nArguments:\n" + "1. hash (string, optional) hash of the block to submit\n" + "2. auxpow (string, optional) serialised auxpow found\n" + "\nResult (without arguments):\n" + "{\n" + " \"hash\" (string) hash of the created block\n" + " \"chainid\" (numeric) chain ID for this block\n" + " \"previousblockhash\" (string) hash of the previous block\n" + " \"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" + "}\n" + "\nResult (with arguments):\n" + "xxxxx (boolean) whether the submitted block was correct\n" + "\nExamples:\n" + + HelpExampleCli("getauxblock", "") + + HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"") + + HelpExampleRpc("getauxblock", "") + ); + + boost::shared_ptr<CReserveScript> coinbaseScript; + GetMainSignals().ScriptForMining(coinbaseScript); + + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbaseScript) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + //throw an error if no script was provided + if (!coinbaseScript->reserveScript.size()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); + + /* Create a new block */ + if (request.params.size() == 0) + return AuxMiningCreateBlock(coinbaseScript->reserveScript); + + /* Submit a block instead. Note that this need not lock cs_main, + since ProcessNewBlock below locks it instead. */ + assert(request.params.size() == 2); + bool fAccepted = AuxMiningSubmitBlock(request.params[0].get_str(), + request.params[1].get_str()); + if (fAccepted) + coinbaseScript->KeepScript(); + + return fAccepted; +} + +UniValue createauxblock(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "createauxblock <address>\n" + "\ncreate a new block and return information required to merge-mine it.\n" + "\nArguments:\n" + "1. address (string, required) specify coinbase transaction payout address\n" + "\nResult:\n" + "{\n" + " \"hash\" (string) hash of the created block\n" + " \"chainid\" (numeric) chain ID for this block\n" + " \"previousblockhash\" (string) hash of the previous block\n" + " \"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" + "}\n" + "\nExamples:\n" + + HelpExampleCli("createauxblock", "\"address\"") + + HelpExampleRpc("createauxblock", "\"address\"") + ); + + // Check coinbase payout address + CBitcoinAddress coinbaseAddress(request.params[0].get_str()); + if (!coinbaseAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Invalid coinbase payout address"); + const CScript scriptPubKey + = GetScriptForDestination(coinbaseAddress.Get()); + + return AuxMiningCreateBlock(scriptPubKey); +} + +UniValue submitauxblock(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 2) + throw std::runtime_error( + "submitauxblock <hash> <auxpow>\n" + "\nsubmit a solved auxpow for a previously block created by 'createauxblock'.\n" + "\nArguments:\n" + "1. hash (string, required) hash of the block to submit\n" + "2. auxpow (string, required) serialised auxpow found\n" + "\nResult:\n" + "xxxxx (boolean) whether the submitted block was correct\n" + "\nExamples:\n" + + HelpExampleCli("submitauxblock", "\"hash\" \"serialised auxpow\"") + + HelpExampleRpc("submitauxblock", "\"hash\" \"serialised auxpow\"") + ); + + return AuxMiningSubmitBlock(request.params[0].get_str(), + request.params[1].get_str()); +} + static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- @@ -933,6 +1191,9 @@ static const CRPCCommand commands[] = { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","priority_delta","fee_delta"} }, { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} }, { "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} }, + { "mining", "getauxblock", &getauxblock, true, {"hash", "auxpow"} }, + { "mining", "createauxblock", &createauxblock, true, {"address"} }, + { "mining", "submitauxblock", &submitauxblock, true, {"hash", "auxpow"} }, { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, |