Skip to content

Commit dcc8332

Browse files
committed
Add generateblock rpc
1 parent e3154aa commit dcc8332

File tree

4 files changed

+197
-31
lines changed

4 files changed

+197
-31
lines changed

src/miner.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
3939
return nNewTime - nOldTime;
4040
}
4141

42+
void RegenerateCommitments(CBlock& block)
43+
{
44+
CMutableTransaction tx{*block.vtx.at(0)};
45+
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
46+
block.vtx.at(0) = MakeTransactionRef(tx);
47+
48+
GenerateCoinbaseCommitment(block, WITH_LOCK(cs_main, return LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus());
49+
50+
block.hashMerkleRoot = BlockMerkleRoot(block);
51+
}
52+
4253
BlockAssembler::Options::Options() {
4354
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
4455
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;

src/miner.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,7 @@ class BlockAssembler
203203
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
204204
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
205205

206+
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
207+
void RegenerateCommitments(CBlock& block);
208+
206209
#endif // BITCOIN_MINER_H

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
3333
{ "generatetoaddress", 2, "maxtries" },
3434
{ "generatetodescriptor", 0, "num_blocks" },
3535
{ "generatetodescriptor", 2, "maxtries" },
36+
{ "generateblock", 1, "transactions" },
3637
{ "getnetworkhashps", 0, "nblocks" },
3738
{ "getnetworkhashps", 1, "height" },
3839
{ "sendtoaddress", 1, "amount" },

src/rpc/mining.cpp

Lines changed: 182 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,36 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
100100
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
101101
}
102102

103+
static bool GenerateBlock(CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
104+
{
105+
block_hash.SetNull();
106+
107+
{
108+
LOCK(cs_main);
109+
IncrementExtraNonce(&block, ::ChainActive().Tip(), extra_nonce);
110+
}
111+
112+
CChainParams chainparams(Params());
113+
114+
while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus()) && !ShutdownRequested()) {
115+
++block.nNonce;
116+
--max_tries;
117+
}
118+
if (max_tries == 0 || ShutdownRequested()) {
119+
return false;
120+
}
121+
if (block.nNonce == std::numeric_limits<uint32_t>::max()) {
122+
return true;
123+
}
124+
125+
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
126+
if (!ProcessNewBlock(chainparams, shared_pblock, true, nullptr))
127+
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
128+
129+
block_hash = block.GetHash();
130+
return true;
131+
}
132+
103133
static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
104134
{
105135
int nHeightEnd = 0;
@@ -118,29 +148,54 @@ static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbas
118148
if (!pblocktemplate.get())
119149
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
120150
CBlock *pblock = &pblocktemplate->block;
121-
{
122-
LOCK(cs_main);
123-
IncrementExtraNonce(pblock, ::ChainActive().Tip(), nExtraNonce);
124-
}
125-
while (nMaxTries > 0 && pblock->nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()) && !ShutdownRequested()) {
126-
++pblock->nNonce;
127-
--nMaxTries;
128-
}
129-
if (nMaxTries == 0 || ShutdownRequested()) {
151+
152+
uint256 block_hash;
153+
if (!GenerateBlock(*pblock, nMaxTries, nExtraNonce, block_hash)) {
130154
break;
131155
}
132-
if (pblock->nNonce == std::numeric_limits<uint32_t>::max()) {
133-
continue;
156+
157+
if (!block_hash.IsNull()) {
158+
++nHeight;
159+
blockHashes.push_back(block_hash.GetHex());
134160
}
135-
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
136-
if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr))
137-
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
138-
++nHeight;
139-
blockHashes.push_back(pblock->GetHash().GetHex());
140161
}
141162
return blockHashes;
142163
}
143164

165+
static bool getScriptFromDescriptor(const std::string& descriptor, CScript& script, std::string& error)
166+
{
167+
FlatSigningProvider key_provider;
168+
const auto desc = Parse(descriptor, key_provider, error, /* require_checksum = */ false);
169+
if (desc) {
170+
if (desc->IsRange()) {
171+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptor not accepted. Maybe pass through deriveaddresses first?");
172+
}
173+
174+
FlatSigningProvider provider;
175+
std::vector<CScript> scripts;
176+
if (!desc->Expand(0, key_provider, scripts, provider)) {
177+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
178+
}
179+
180+
// Combo desriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
181+
CHECK_NONFATAL(scripts.size() > 0 && scripts.size() <= 4);
182+
183+
if (scripts.size() == 1) {
184+
script = scripts.at(0);
185+
} else if (scripts.size() == 4) {
186+
// For uncompressed keys, take the 3rd script, since it is p2wpkh
187+
script = scripts.at(2);
188+
} else {
189+
// Else take the 2nd script, since it is p2pkh
190+
script = scripts.at(1);
191+
}
192+
193+
return true;
194+
} else {
195+
return false;
196+
}
197+
}
198+
144199
static UniValue generatetodescriptor(const JSONRPCRequest& request)
145200
{
146201
RPCHelpMan{
@@ -165,27 +220,15 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
165220
const int num_blocks{request.params[0].get_int()};
166221
const int64_t max_tries{request.params[2].isNull() ? 1000000 : request.params[2].get_int()};
167222

168-
FlatSigningProvider key_provider;
223+
CScript coinbase_script;
169224
std::string error;
170-
const auto desc = Parse(request.params[1].get_str(), key_provider, error, /* require_checksum = */ false);
171-
if (!desc) {
225+
if (!getScriptFromDescriptor(request.params[1].get_str(), coinbase_script, error)) {
172226
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
173227
}
174-
if (desc->IsRange()) {
175-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptor not accepted. Maybe pass through deriveaddresses first?");
176-
}
177-
178-
FlatSigningProvider provider;
179-
std::vector<CScript> coinbase_script;
180-
if (!desc->Expand(0, key_provider, coinbase_script, provider)) {
181-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
182-
}
183228

184229
const CTxMemPool& mempool = EnsureMemPool();
185230

186-
CHECK_NONFATAL(coinbase_script.size() == 1);
187-
188-
return generateBlocks(mempool, coinbase_script.at(0), num_blocks, max_tries);
231+
return generateBlocks(mempool, coinbase_script, num_blocks, max_tries);
189232
}
190233

191234
static UniValue generatetoaddress(const JSONRPCRequest& request)
@@ -228,6 +271,113 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
228271
return generateBlocks(mempool, coinbase_script, nGenerate, nMaxTries);
229272
}
230273

274+
static UniValue generateblock(const JSONRPCRequest& request)
275+
{
276+
RPCHelpMan{"generateblock",
277+
"\nMine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n",
278+
{
279+
{"address/descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."},
280+
{"transactions", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings which are either txids or raw transactions.\n"
281+
"Txids must reference transactions currently in the mempool.\n"
282+
"All transactions must be valid and in valid order, otherwise the block will be rejected.",
283+
{
284+
{"rawtx/txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
285+
},
286+
}
287+
},
288+
RPCResult{
289+
RPCResult::Type::OBJ, "", "",
290+
{
291+
{RPCResult::Type::STR_HEX, "hash", "hash of generated block"}
292+
}
293+
},
294+
RPCExamples{
295+
"\nGenerate a block to myaddress, with txs rawtx and mempool_txid\n"
296+
+ HelpExampleCli("generateblock", R"("myaddress" '["rawtx", "mempool_txid"]')")
297+
},
298+
}.Check(request);
299+
300+
const auto address_or_descriptor = request.params[0].get_str();
301+
CScript coinbase_script;
302+
std::string error;
303+
304+
if (!getScriptFromDescriptor(address_or_descriptor, coinbase_script, error)) {
305+
const auto destination = DecodeDestination(address_or_descriptor);
306+
if (!IsValidDestination(destination)) {
307+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address or descriptor");
308+
}
309+
310+
coinbase_script = GetScriptForDestination(destination);
311+
}
312+
313+
const CTxMemPool& mempool = EnsureMemPool();
314+
315+
std::vector<CTransactionRef> txs;
316+
const auto raw_txs_or_txids = request.params[1].get_array();
317+
for (size_t i = 0; i < raw_txs_or_txids.size(); i++) {
318+
const auto str(raw_txs_or_txids[i].get_str());
319+
320+
uint256 hash;
321+
CMutableTransaction mtx;
322+
if (ParseHashStr(str, hash)) {
323+
324+
const auto tx = mempool.get(hash);
325+
if (!tx) {
326+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Transaction %s not in mempool.", str));
327+
}
328+
329+
txs.emplace_back(tx);
330+
331+
} else if (DecodeHexTx(mtx, str)) {
332+
txs.push_back(MakeTransactionRef(std::move(mtx)));
333+
334+
} else {
335+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("Transaction decode failed for %s", str));
336+
}
337+
}
338+
339+
CChainParams chainparams(Params());
340+
CBlock block;
341+
342+
{
343+
LOCK(cs_main);
344+
345+
CTxMemPool empty_mempool;
346+
std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(coinbase_script));
347+
if (!blocktemplate) {
348+
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
349+
}
350+
block = blocktemplate->block;
351+
}
352+
353+
CHECK_NONFATAL(block.vtx.size() == 1);
354+
355+
// Add transactions
356+
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
357+
RegenerateCommitments(block);
358+
359+
{
360+
LOCK(cs_main);
361+
362+
BlockValidationState state;
363+
if (!TestBlockValidity(state, chainparams, block, LookupBlockIndex(block.hashPrevBlock), false, false)) {
364+
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
365+
}
366+
}
367+
368+
uint256 block_hash;
369+
uint64_t max_tries{1000000};
370+
unsigned int extra_nonce{0};
371+
372+
if (!GenerateBlock(block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) {
373+
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
374+
}
375+
376+
UniValue obj(UniValue::VOBJ);
377+
obj.pushKV("hash", block_hash.GetHex());
378+
return obj;
379+
}
380+
231381
static UniValue getmininginfo(const JSONRPCRequest& request)
232382
{
233383
RPCHelpMan{"getmininginfo",
@@ -1035,6 +1185,7 @@ static const CRPCCommand commands[] =
10351185

10361186
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },
10371187
{ "generating", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} },
1188+
{ "generating", "generateblock", &generateblock, {"address","transactions"} },
10381189

10391190
{ "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} },
10401191

0 commit comments

Comments
 (0)