aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/auxpow_miner.cpp165
-rw-r--r--src/rpc/auxpow_miner.h95
-rw-r--r--src/rpc/blockchain.cpp43
-rw-r--r--src/rpc/mining.cpp81
-rw-r--r--src/rpc/mining.h7
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