Skip to content

Commit

Permalink
Merge #2297: [RPC] Implement generatetoaddress RPC command
Browse files Browse the repository at this point in the history
4fe3b03 [RPC] Implement generatetoaddress RPC command (furszy)
d617467 [RPC] Subdivide `generate` RPC command. (furszy)
50d99bb [Miner] Enable PoW block creations with custom coinbase script (furszy)

Pull request description:

  Introducing a new RPC command `generatetoaddress`. Useful mostly for functional testing and a good number of updates that are coming from upstream.

ACKs for top commit:
  Fuzzbawls:
    ACK 4fe3b03
  random-zebra:
    utACK 4fe3b03 and merging...

Tree-SHA512: aeba14cd2607ec06c15d5673fc77633039aa829295d5ad40bd134f29f2046d942796171449f8a46ac4b21bfc08b5ea70df080966b8fad7fc377662e9239ec732
  • Loading branch information
random-zebra committed May 13, 2021
2 parents 086fc30 + 4fe3b03 commit f67f18d
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 51 deletions.
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,
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 @@ -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
Expand Down

0 comments on commit f67f18d

Please sign in to comment.