diff options
| author | Max K <[email protected]> | 2019-07-14 19:35:30 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-07-14 19:35:30 +0200 |
| commit | cee13699a5676355487f8eb2d91985f63438eae4 (patch) | |
| tree | cf12be6180f950a25ee2ee7f3f2126542835d6e3 /src/rpc | |
| parent | Correct build and test net seed (diff) | |
| parent | Handle legacy v2 block at #66064 (diff) | |
| download | discoin-1.17-dev.tar.xz discoin-1.17-dev.zip | |
Merge pull request #1546 from rnicoll/1.17-auxpow1.17-dev
1.17 AuxPoW support
Diffstat (limited to 'src/rpc')
| -rw-r--r-- | src/rpc/auxpow_miner.cpp | 165 | ||||
| -rw-r--r-- | src/rpc/auxpow_miner.h | 95 | ||||
| -rw-r--r-- | src/rpc/blockchain.cpp | 43 | ||||
| -rw-r--r-- | src/rpc/mining.cpp | 81 | ||||
| -rw-r--r-- | src/rpc/mining.h | 7 |
5 files changed, 381 insertions, 10 deletions
diff --git a/src/rpc/auxpow_miner.cpp b/src/rpc/auxpow_miner.cpp new file mode 100644 index 000000000..3d6e93bac --- /dev/null +++ b/src/rpc/auxpow_miner.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2018 Daniel Kraft +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <rpc/auxpow_miner.h> + +#include <arith_uint256.h> +#include <auxpow.h> +#include <chainparams.h> +#include <net.h> +#include <rpc/protocol.h> +#include <utilstrencodings.h> +#include <utiltime.h> +#include <validation.h> + +#include <cassert> + +namespace +{ + +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); + const auto auxpowStart = Params ().GetConsensus ().nAuxpowStartHeight; + if (chainActive.Height () + 1 < auxpowStart) + throw std::runtime_error ("mining auxblock method is not yet available"); + } +} + +} // anonymous namespace + +const CBlock* +AuxpowMiner::getCurrentBlock (const CScript& scriptPubKey, uint256& target) +{ + AssertLockHeld (cs); + + { + LOCK (cs_main); + if (pindexPrev != chainActive.Tip () + || (mempool.GetTransactionsUpdated () != txUpdatedLast + && GetTime () - startTime > 60)) + { + if (pindexPrev != chainActive.Tip ()) + { + /* Clear old blocks since they're obsolete now. */ + blocks.clear (); + templates.clear (); + pblockCur = nullptr; + } + + /* Create new block with nonce = 0 and extraNonce = 1. */ + std::unique_ptr<CBlockTemplate> newBlock + = BlockAssembler (Params ()).CreateNewBlock (scriptPubKey); + if (newBlock == nullptr) + throw JSONRPCError (RPC_OUT_OF_MEMORY, "out of memory"); + + /* Update state only when CreateNewBlock succeeded. */ + txUpdatedLast = mempool.GetTransactionsUpdated (); + pindexPrev = chainActive.Tip (); + startTime = GetTime (); + + /* Finalise it by setting the version and building the merkle root. */ + IncrementExtraNonce (&newBlock->block, pindexPrev, extraNonce); + newBlock->block.SetAuxpowFlag (true); + + /* Save in our map of constructed blocks. */ + pblockCur = &newBlock->block; + blocks[pblockCur->GetHash ()] = pblockCur; + templates.push_back (std::move (newBlock)); + } + } + + /* At this point, pblockCur 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 pblockCur in a previous call, as pindexPrev is + initialised only when pblockCur is. */ + assert (pblockCur); + + arith_uint256 arithTarget; + bool fNegative, fOverflow; + arithTarget.SetCompact (pblockCur->nBits, &fNegative, &fOverflow); + if (fNegative || fOverflow || arithTarget == 0) + throw std::runtime_error ("invalid difficulty bits in block"); + target = ArithToUint256 (arithTarget); + + return pblockCur; +} + +const CBlock* +AuxpowMiner::lookupSavedBlock (const std::string& hashHex) const +{ + AssertLockHeld (cs); + + uint256 hash; + hash.SetHex (hashHex); + + const auto iter = blocks.find (hash); + if (iter == blocks.end ()) + throw JSONRPCError (RPC_INVALID_PARAMETER, "block hash unknown"); + + return iter->second; +} + +UniValue +AuxpowMiner::createAuxBlock (const CScript& scriptPubKey) +{ + auxMiningCheck (); + LOCK (cs); + + uint256 target; + const CBlock* pblock = getCurrentBlock (scriptPubKey, target); + + UniValue result(UniValue::VOBJ); + result.pushKV ("hash", pblock->GetHash ().GetHex ()); + result.pushKV ("chainid", pblock->GetChainId ()); + result.pushKV ("previousblockhash", pblock->hashPrevBlock.GetHex ()); + result.pushKV ("coinbasevalue", + static_cast<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; +} + +bool +AuxpowMiner::submitAuxBlock (const std::string& hashHex, + const std::string& auxpowHex) const +{ + auxMiningCheck (); + + std::shared_ptr<CBlock> shared_block; + { + LOCK (cs); + const CBlock* pblock = lookupSavedBlock (hashHex); + shared_block = std::make_shared<CBlock> (*pblock); + } + + const std::vector<unsigned char> vchAuxPow = ParseHex (auxpowHex); + CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); + std::unique_ptr<CAuxPow> pow(new CAuxPow ()); + ss >> *pow; + shared_block->SetAuxpow (std::move (pow)); + assert (shared_block->GetHash ().GetHex () == hashHex); + + return ProcessNewBlock (Params (), shared_block, true, nullptr); +} diff --git a/src/rpc/auxpow_miner.h b/src/rpc/auxpow_miner.h new file mode 100644 index 000000000..159495730 --- /dev/null +++ b/src/rpc/auxpow_miner.h @@ -0,0 +1,95 @@ +// Copyright (c) 2018 Daniel Kraft +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_AUXPOW_MINER_H +#define BITCOIN_RPC_AUXPOW_MINER_H + +#include <miner.h> +#include <script/script.h> +#include <sync.h> +#include <uint256.h> +#include <univalue.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace auxpow_tests +{ +class AuxpowMinerForTest; +} + +/** + * This class holds "global" state used to construct blocks for the auxpow + * mining RPCs and the map of already constructed blocks to look them up + * in the submitauxblock RPC. + * + * It is used as a singleton that is initialised during startup, taking the + * place of the previously real global and static variables. + */ +class AuxpowMiner +{ + +private: + + /** The lock used for state in this object. */ + mutable CCriticalSection cs; + /** All currently "active" block templates. */ + std::vector<std::unique_ptr<CBlockTemplate>> templates; + /** Maps block hashes to pointers in vTemplates. Does not own the memory. */ + std::map<uint256, const CBlock*> blocks; + + /** + * The block we are "currently" working on. This does not own the memory, + * instead, it points into an element of templates. + */ + CBlock* pblockCur = nullptr; + /** The current extra nonce for block creation. */ + unsigned extraNonce = 0; + + /* Some data about when the current block (pblock) was constructed. */ + unsigned txUpdatedLast; + const CBlockIndex* pindexPrev = nullptr; + uint64_t startTime; + + /** + * Constructs a new current block if necessary (checking the current state to + * see if "enough changed" for this), and returns a pointer to the block + * that should be returned to a miner for working on at the moment. Also + * fills in the difficulty target value. + */ + const CBlock* getCurrentBlock (const CScript& scriptPubKey, uint256& target); + + /** + * Looks up a previously constructed block by its (hex-encoded) hash. If the + * block is found, it is returned. Otherwise, a JSONRPCError is thrown. + */ + const CBlock* lookupSavedBlock (const std::string& hashHex) const; + + friend class auxpow_tests::AuxpowMinerForTest; + +public: + + AuxpowMiner () = default; + + /** + * Performs the main work for the "createauxblock" RPC: Construct a new block + * to work on with the given address for the block reward and return the + * necessary information for the miner to construct an auxpow for it. + */ + UniValue createAuxBlock (const CScript& scriptPubKey); + + /** + * Performs the main work for the "submitauxblock" RPC: Look up the block + * previously created for the given hash, attach the given auxpow to it + * and try to submit it. Returns true if all was successful and the block + * was accepted. + */ + bool submitAuxBlock (const std::string& hashHex, + const std::string& auxpowHex) const; + +}; + +#endif // BITCOIN_RPC_AUXPOW_MINER_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 948553d9a..3309a9701 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -80,6 +80,42 @@ double GetDifficulty(const CBlockIndex* blockindex) return dDiff; } +UniValue AuxpowToJSON(const CAuxPow& auxpow) +{ + UniValue result(UniValue::VOBJ); + + { + UniValue tx(UniValue::VOBJ); + tx.pushKV("hex", EncodeHexTx(*auxpow.coinbaseTx.tx)); + TxToUniv(*auxpow.coinbaseTx.tx, auxpow.parentBlock.GetHash(), tx); + result.pushKV("tx", tx); + } + + result.pushKV("index", auxpow.coinbaseTx.nIndex); + result.pushKV("chainindex", auxpow.nChainIndex); + + { + UniValue branch(UniValue::VARR); + for (const auto& node : auxpow.coinbaseTx.vMerkleBranch) + branch.push_back(node.GetHex()); + result.pushKV("merklebranch", branch); + } + + { + UniValue branch(UniValue::VARR); + for (const auto& node : auxpow.vChainMerkleBranch) + branch.push_back(node.GetHex()); + result.pushKV("chainmerklebranch", branch); + } + + CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION); + ssParent << auxpow.parentBlock; + const std::string strHex = HexStr(ssParent.begin(), ssParent.end()); + result.pushKV("parentblock", strHex); + + return result; +} + UniValue blockheaderToJSON(const CBlockIndex* blockindex) { AssertLockHeld(cs_main); @@ -148,6 +184,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.pushKV("chainwork", blockindex->nChainWork.GetHex()); result.pushKV("nTx", (uint64_t)blockindex->nTx); + if (block.auxpow) + result.pushKV("auxpow", AuxpowToJSON(*block.auxpow)); + if (blockindex->pprev) result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); CBlockIndex *pnext = chainActive.Next(blockindex); @@ -729,7 +768,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) if (!fVerbose) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); + ssBlock << pblockindex->GetBlockHeader(Params().GetConsensus()); std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; } @@ -1270,7 +1309,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { + for (int pos = Consensus::DEPLOYMENT_TESTDUMMY + 1; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos)); } obj.pushKV("softforks", softforks); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 0622e3b72..75c71a323 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -16,6 +16,7 @@ #include <net.h> #include <policy/fees.h> #include <pow.h> +#include <rpc/auxpow_miner.h> #include <rpc/blockchain.h> #include <rpc/mining.h> #include <rpc/server.h> @@ -26,8 +27,9 @@ #include <validationinterface.h> #include <warnings.h> -#include <memory> #include <stdint.h> +#include <string> +#include <utility> unsigned int ParseConfirmTarget(const UniValue& value) { @@ -126,14 +128,15 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } - while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetPoWHash(), pblock->nBits, Params().GetConsensus())) { - ++pblock->nNonce; + auto& miningHeader = CAuxPow::initAuxPow(*pblock); + 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); @@ -489,11 +492,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? } - const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT]; // If the caller is indicating segwit support, then allow CreateNewBlock() // to select witness transactions, after segwit activates (otherwise // don't). - bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end(); + bool fSupportsSegwit = setClientRules.find("segwit") != setClientRules.end(); // Update block static CBlockIndex* pindexPrev; @@ -533,7 +535,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) pblock->nNonce = 0; // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration - const bool fPreSegWit = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache)); + const bool fPreSegWit = !IsWitnessEnabled(pindexPrev, consensusParams); UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); @@ -932,6 +934,68 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) return result; } +/* ************************************************************************** */ +/* Merge mining. */ + +std::unique_ptr<AuxpowMiner> g_auxpow_miner; + +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\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("createauxblock", "\"address\"") + + HelpExampleRpc("createauxblock", "\"address\"") + ); + + // Check coinbase payout address + const CTxDestination coinbaseScript + = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(coinbaseScript)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Error: Invalid coinbase payout address"); + } + const CScript scriptPubKey = GetScriptForDestination(coinbaseScript); + + return g_auxpow_miner->createAuxBlock(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 g_auxpow_miner->submitAuxBlock(request.params[0].get_str(), + request.params[1].get_str()); +} + +/* ************************************************************************** */ + static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -940,7 +1004,8 @@ static const CRPCCommand commands[] = { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} }, { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, - + { "mining", "createauxblock", &createauxblock, {"address"} }, + { "mining", "submitauxblock", &submitauxblock, {"hash", "auxpow"} }, { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, diff --git a/src/rpc/mining.h b/src/rpc/mining.h index 8d4627315..759d35e64 100644 --- a/src/rpc/mining.h +++ b/src/rpc/mining.h @@ -9,10 +9,17 @@ #include <univalue.h> +#include <memory> + +class AuxpowMiner; + /** Generate blocks (mine) */ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript); /** Check bounds on a command line confirm target */ unsigned int ParseConfirmTarget(const UniValue& value); +/** Singleton instance of the AuxpowMiner, created during startup. */ +extern std::unique_ptr<AuxpowMiner> g_auxpow_miner; + #endif |