diff --git a/src/miner.cpp b/src/miner.cpp index 3d87d2688ef95..68291fc60ef20 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -42,9 +42,12 @@ int64_t nHPSTimerStart = 0; std::unique_ptr CreateNewBlockWithKey(CReserveKey* reservekey, CWallet* pwallet) { CPubKey pubkey; - if (!reservekey->GetReservedKey(pubkey)) - return nullptr; + if (!reservekey->GetReservedKey(pubkey)) return nullptr; + return CreateNewBlockWithScript(GetScriptForDestination(pubkey.GetID()), pwallet); +} +std::unique_ptr CreateNewBlockWithScript(const CScript& coinbaseScript, CWallet* pwallet) +{ const int nHeightNext = chainActive.Tip()->nHeight + 1; // If we're building a late PoW block, don't continue @@ -56,8 +59,7 @@ std::unique_ptr CreateNewBlockWithKey(CReserveKey* reservekey, C return nullptr; } - CScript scriptPubKey = GetScriptForDestination(pubkey.GetID()); - return BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwallet, false); + return BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(coinbaseScript, pwallet, false); } bool ProcessBlockFound(const std::shared_ptr& pblock, CWallet& wallet, Optional& reservekey) diff --git a/src/miner.h b/src/miner.h index c4feb04cfe09f..6a141e9920678 100644 --- a/src/miner.h +++ b/src/miner.h @@ -26,8 +26,9 @@ static const bool DEFAULT_PRINTPRIORITY = false; #ifdef ENABLE_WALLET /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads); - /** Generate a new block, without valid proof-of-work */ + /** Generate a new PoW block, without valid proof-of-work */ std::unique_ptr CreateNewBlockWithKey(CReserveKey* reservekey, CWallet* pwallet); + std::unique_ptr CreateNewBlockWithScript(const CScript& coinbaseScript, CWallet* pwallet); void BitcoinMiner(CWallet* pwallet, bool fProofOfStake); void ThreadStakeMinter(); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index e360d3fd3f275..c61591c2cc99e 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -27,6 +27,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { { "setgenerate", 0 }, { "setgenerate", 1 }, { "generate", 0 }, + { "generatetoaddress", 0 }, { "getnetworkhashps", 0 }, { "getnetworkhashps", 1 }, { "delegatestake", 1 }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f0d2aebf286a3..1d1a7f715f7a2 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -6,6 +6,7 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php. #include "amount.h" +#include "base58.h" #include "blockassembler.h" #include "chainparams.h" #include "core_io.h" @@ -14,19 +15,74 @@ #include "net.h" #include "pow.h" #include "rpc/server.h" -#include "util.h" #include "validationinterface.h" #ifdef ENABLE_WALLET #include "wallet/db.h" #include "wallet/wallet.h" #endif -#include "warnings.h" #include #include #ifdef ENABLE_WALLET +UniValue generateBlocks(const Consensus::Params& consensus, + CWallet* wallet, + bool fPoS, + const int nGenerate, + int nHeight, + int nHeightEnd, + CScript* coinbaseScript) +{ + UniValue blockHashes(UniValue::VARR); + unsigned int nExtraNonce = 0; + + while (nHeight < nHeightEnd && !ShutdownRequested()) { + + // Get available coins + std::vector availableCoins; + if (fPoS && !wallet->StakeableCoins(&availableCoins)) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available coins to stake"); + } + + std::unique_ptr pblocktemplate(fPoS ? + BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), wallet, true, &availableCoins) : + CreateNewBlockWithScript(*coinbaseScript, wallet)); + if (!pblocktemplate.get()) break; + std::shared_ptr pblock = std::make_shared(pblocktemplate->block); + + if(!fPoS) { + { + LOCK(cs_main); + IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); + } + while (pblock->nNonce < std::numeric_limits::max() && + !CheckProofOfWork(pblock->GetHash(), pblock->nBits)) { + ++pblock->nNonce; + } + if (ShutdownRequested()) break; + if (pblock->nNonce == std::numeric_limits::max()) continue; + } + + CValidationState state; + if (!ProcessNewBlock(state, nullptr, pblock, nullptr)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); + + ++nHeight; + blockHashes.push_back(pblock->GetHash().GetHex()); + + // Check PoS if needed. + if (!fPoS) + fPoS = consensus.NetworkUpgradeActive(nHeight + 1, Consensus::UPGRADE_POS); + } + + const int nGenerated = blockHashes.size(); + if (nGenerated == 0 || (!fPoS && nGenerated < nGenerate)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new blocks"); + + return blockHashes; +} + UniValue generate(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) @@ -62,6 +118,7 @@ UniValue generate(const JSONRPCRequest& request) const Consensus::Params& consensus = Params().GetConsensus(); bool fPoS = consensus.NetworkUpgradeActive(nHeight + 1, Consensus::UPGRADE_POS); std::unique_ptr reservekey; + CScript coinbaseScript; if (fPoS) { // If we are in PoS, wallet must be unlocked. @@ -69,62 +126,72 @@ UniValue generate(const JSONRPCRequest& request) } else { // Coinbase key reservekey = MakeUnique(pwalletMain); + CPubKey pubkey; + if (!reservekey->GetReservedKey(pubkey)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Error: Cannot get key from keypool"); + coinbaseScript = GetScriptForDestination(pubkey.GetID()); } - UniValue blockHashes(UniValue::VARR); - unsigned int nExtraNonce = 0; - - while (nHeight < nHeightEnd && !ShutdownRequested()) { - - // Get available coins - std::vector availableCoins; - if (fPoS && !pwalletMain->StakeableCoins(&availableCoins)) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available coins to stake"); - } - - std::unique_ptr pblocktemplate((fPoS ? - BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwalletMain, true, &availableCoins) : - CreateNewBlockWithKey(reservekey.get(), pwalletMain))); - if (!pblocktemplate.get()) break; - std::shared_ptr pblock = std::make_shared(pblocktemplate->block); + // Create the blocks + UniValue blockHashes = generateBlocks(consensus, + pwalletMain, + fPoS, + nGenerate, + nHeight, + nHeightEnd, + &coinbaseScript); - if(!fPoS) { - { - LOCK(cs_main); - IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); - } - while (pblock->nNonce < std::numeric_limits::max() && - !CheckProofOfWork(pblock->GetHash(), pblock->nBits)) { - ++pblock->nNonce; - } - if (ShutdownRequested()) break; - if (pblock->nNonce == std::numeric_limits::max()) continue; - } + // mark key as used, only for PoW coinbases + if (reservekey) { + // Remove key from key pool + reservekey->KeepKey(); + } - CValidationState state; - if (!ProcessNewBlock(state, nullptr, pblock, nullptr)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); + return blockHashes; +} - ++nHeight; - blockHashes.push_back(pblock->GetHash().GetHex()); +UniValue generatetoaddress(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 2) + throw std::runtime_error( + "generatetoaddress numblocks \"address\"\n" + "\nMine blocks immediately to a specified address (before the RPC call returns)\n" + "\nArguments:\n" + "1. numblocks (numeric, required) How many blocks are generated immediately.\n" + "2. \"address\" (string, required) The address to send the newly generated bitcoin to.\n" + "\nResult\n" + "[ blockhashes ] (array) hashes of blocks generated\n" + "\nExamples:\n" + "\nGenerate 11 blocks to myaddress\n" + + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") + ); - // Check PoS if needed. - if (!fPoS) - fPoS = consensus.NetworkUpgradeActive(nHeight + 1, Consensus::UPGRADE_POS); + int nGenerate = request.params[0].get_int(); + CTxDestination dest(DecodeDestination(request.params[1].get_str())); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); } + CScript coinbaseScript = GetScriptForDestination(dest); - const int nGenerated = blockHashes.size(); - if (nGenerated == 0 || (!fPoS && nGenerated < nGenerate)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new blocks"); + const Consensus::Params& consensus = Params().GetConsensus(); + int nHeightEnd = 0; + int nHeight = 0; - // mark key as used, only for PoW coinbases - if (reservekey) { - // Remove key from key pool - reservekey->KeepKey(); + { // Don't keep cs_main locked + LOCK(cs_main); + nHeight = chainActive.Height(); + nHeightEnd = nHeight + nGenerate; } - return blockHashes; + bool fPoS = consensus.NetworkUpgradeActive(nHeight + 1, Consensus::UPGRADE_POS); + return generateBlocks(consensus, + pwalletMain, + fPoS, + nGenerate, + nHeight, + nHeightEnd, + &coinbaseScript); } + #endif // ENABLE_WALLET #ifdef ENABLE_MINING_RPC @@ -812,6 +879,7 @@ static const CRPCCommand commands[] = /* Not shown in help */ #ifdef ENABLE_WALLET { "hidden", "generate", &generate, true }, + { "hidden", "generatetoaddress", &generatetoaddress, true }, #endif { "hidden", "submitblock", &submitblock, true }, #ifdef ENABLE_MINING_RPC