Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RPC] Implement generatetoaddress RPC command #2297

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ int64_t nHPSTimerStart = 0;
std::unique_ptr<CBlockTemplate> 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<CBlockTemplate> CreateNewBlockWithScript(const CScript& coinbaseScript, CWallet* pwallet)
{
const int nHeightNext = chainActive.Tip()->nHeight + 1;

// If we're building a late PoW block, don't continue
Expand All @@ -56,8 +59,7 @@ std::unique_ptr<CBlockTemplate> 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<const CBlock>& pblock, CWallet& wallet, Optional<CReserveKey>& reservekey)
Expand Down
3 changes: 2 additions & 1 deletion src/miner.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<CBlockTemplate> CreateNewBlockWithKey(CReserveKey* reservekey, CWallet* pwallet);
std::unique_ptr<CBlockTemplate> CreateNewBlockWithScript(const CScript& coinbaseScript, CWallet* pwallet);

void BitcoinMiner(CWallet* pwallet, bool fProofOfStake);
void ThreadStakeMinter();
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static const CRPCConvertParam vRPCConvertParams[] = {
{ "setgenerate", 0 },
{ "setgenerate", 1 },
{ "generate", 0 },
{ "generatetoaddress", 0 },
{ "getnetworkhashps", 0 },
{ "getnetworkhashps", 1 },
{ "delegatestake", 1 },
Expand Down
160 changes: 114 additions & 46 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 <stdint.h>

#include <univalue.h>

#ifdef ENABLE_WALLET
UniValue generateBlocks(const Consensus::Params& consensus,
furszy marked this conversation as resolved.
Show resolved Hide resolved
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<CStakeableOutput> availableCoins;
if (fPoS && !wallet->StakeableCoins(&availableCoins)) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available coins to stake");
}

std::unique_ptr<CBlockTemplate> pblocktemplate(fPoS ?
BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), wallet, true, &availableCoins) :
CreateNewBlockWithScript(*coinbaseScript, wallet));
if (!pblocktemplate.get()) break;
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(pblocktemplate->block);

if(!fPoS) {
{
LOCK(cs_main);
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
}
while (pblock->nNonce < std::numeric_limits<uint32_t>::max() &&
!CheckProofOfWork(pblock->GetHash(), pblock->nBits)) {
++pblock->nNonce;
}
if (ShutdownRequested()) break;
if (pblock->nNonce == std::numeric_limits<uint32_t>::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)
Expand Down Expand Up @@ -62,69 +118,80 @@ UniValue generate(const JSONRPCRequest& request)
const Consensus::Params& consensus = Params().GetConsensus();
bool fPoS = consensus.NetworkUpgradeActive(nHeight + 1, Consensus::UPGRADE_POS);
std::unique_ptr<CReserveKey> reservekey;
CScript coinbaseScript;

if (fPoS) {
// If we are in PoS, wallet must be unlocked.
EnsureWalletIsUnlocked();
} else {
// Coinbase key
reservekey = MakeUnique<CReserveKey>(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<CStakeableOutput> availableCoins;
if (fPoS && !pwalletMain->StakeableCoins(&availableCoins)) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available coins to stake");
}

std::unique_ptr<CBlockTemplate> pblocktemplate((fPoS ?
BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwalletMain, true, &availableCoins) :
CreateNewBlockWithKey(reservekey.get(), pwalletMain)));
if (!pblocktemplate.get()) break;
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(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<uint32_t>::max() &&
!CheckProofOfWork(pblock->GetHash(), pblock->nBits)) {
++pblock->nNonce;
}
if (ShutdownRequested()) break;
if (pblock->nNonce == std::numeric_limits<uint32_t>::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
Expand Down Expand Up @@ -813,6 +880,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
Expand Down