@@ -101,6 +101,36 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
101
101
return GetNetworkHashPS (!request.params [0 ].isNull () ? request.params [0 ].get_int () : 120 , !request.params [1 ].isNull () ? request.params [1 ].get_int () : -1 );
102
102
}
103
103
104
+ static bool GenerateBlock (CBlock& block, uint64_t & max_tries, unsigned int & extra_nonce, uint256& block_hash)
105
+ {
106
+ block_hash.SetNull ();
107
+
108
+ {
109
+ LOCK (cs_main);
110
+ IncrementExtraNonce (&block, ::ChainActive ().Tip (), extra_nonce);
111
+ }
112
+
113
+ CChainParams chainparams (Params ());
114
+
115
+ while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t >::max () && !CheckProofOfWork (block.GetHash (), block.nBits , chainparams.GetConsensus ()) && !ShutdownRequested ()) {
116
+ ++block.nNonce ;
117
+ --max_tries;
118
+ }
119
+ if (max_tries == 0 || ShutdownRequested ()) {
120
+ return false ;
121
+ }
122
+ if (block.nNonce == std::numeric_limits<uint32_t >::max ()) {
123
+ return true ;
124
+ }
125
+
126
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
127
+ if (!ProcessNewBlock (chainparams, shared_pblock, true , nullptr ))
128
+ throw JSONRPCError (RPC_INTERNAL_ERROR, " ProcessNewBlock, block not accepted" );
129
+
130
+ block_hash = block.GetHash ();
131
+ return true ;
132
+ }
133
+
104
134
static UniValue generateBlocks (const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
105
135
{
106
136
int nHeightEnd = 0 ;
@@ -119,29 +149,54 @@ static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbas
119
149
if (!pblocktemplate.get ())
120
150
throw JSONRPCError (RPC_INTERNAL_ERROR, " Couldn't create new block" );
121
151
CBlock *pblock = &pblocktemplate->block ;
122
- {
123
- LOCK (cs_main);
124
- IncrementExtraNonce (pblock, ::ChainActive ().Tip (), nExtraNonce);
125
- }
126
- while (nMaxTries > 0 && pblock->nNonce < std::numeric_limits<uint32_t >::max () && !CheckProofOfWork (pblock->GetHash (), pblock->nBits , Params ().GetConsensus ()) && !ShutdownRequested ()) {
127
- ++pblock->nNonce ;
128
- --nMaxTries;
129
- }
130
- if (nMaxTries == 0 || ShutdownRequested ()) {
152
+
153
+ uint256 block_hash;
154
+ if (!GenerateBlock (*pblock, nMaxTries, nExtraNonce, block_hash)) {
131
155
break ;
132
156
}
133
- if (pblock->nNonce == std::numeric_limits<uint32_t >::max ()) {
134
- continue ;
157
+
158
+ if (!block_hash.IsNull ()) {
159
+ ++nHeight;
160
+ blockHashes.push_back (block_hash.GetHex ());
135
161
}
136
- std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
137
- if (!ProcessNewBlock (Params (), shared_pblock, true , nullptr ))
138
- throw JSONRPCError (RPC_INTERNAL_ERROR, " ProcessNewBlock, block not accepted" );
139
- ++nHeight;
140
- blockHashes.push_back (pblock->GetHash ().GetHex ());
141
162
}
142
163
return blockHashes;
143
164
}
144
165
166
+ static bool getScriptFromDescriptor (const std::string& descriptor, CScript& script, std::string& error)
167
+ {
168
+ FlatSigningProvider key_provider;
169
+ const auto desc = Parse (descriptor, key_provider, error, /* require_checksum = */ false );
170
+ if (desc) {
171
+ if (desc->IsRange ()) {
172
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Ranged descriptor not accepted. Maybe pass through deriveaddresses first?" );
173
+ }
174
+
175
+ FlatSigningProvider provider;
176
+ std::vector<CScript> scripts;
177
+ if (!desc->Expand (0 , key_provider, scripts, provider)) {
178
+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, strprintf (" Cannot derive script without private keys" ));
179
+ }
180
+
181
+ // Combo desriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
182
+ CHECK_NONFATAL (scripts.size () > 0 && scripts.size () <= 4 );
183
+
184
+ if (scripts.size () == 1 ) {
185
+ script = scripts.at (0 );
186
+ } else if (scripts.size () == 4 ) {
187
+ // For uncompressed keys, take the 3rd script, since it is p2wpkh
188
+ script = scripts.at (2 );
189
+ } else {
190
+ // Else take the 2nd script, since it is p2pkh
191
+ script = scripts.at (1 );
192
+ }
193
+
194
+ return true ;
195
+ } else {
196
+ return false ;
197
+ }
198
+ }
199
+
145
200
static UniValue generatetodescriptor (const JSONRPCRequest& request)
146
201
{
147
202
RPCHelpMan{
@@ -166,27 +221,15 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
166
221
const int num_blocks{request.params [0 ].get_int ()};
167
222
const int64_t max_tries{request.params [2 ].isNull () ? 1000000 : request.params [2 ].get_int ()};
168
223
169
- FlatSigningProvider key_provider ;
224
+ CScript coinbase_script ;
170
225
std::string error;
171
- const auto desc = Parse (request.params [1 ].get_str (), key_provider, error, /* require_checksum = */ false );
172
- if (!desc) {
226
+ if (!getScriptFromDescriptor (request.params [1 ].get_str (), coinbase_script, error)) {
173
227
throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, error);
174
228
}
175
- if (desc->IsRange ()) {
176
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Ranged descriptor not accepted. Maybe pass through deriveaddresses first?" );
177
- }
178
-
179
- FlatSigningProvider provider;
180
- std::vector<CScript> coinbase_script;
181
- if (!desc->Expand (0 , key_provider, coinbase_script, provider)) {
182
- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, strprintf (" Cannot derive script without private keys" ));
183
- }
184
229
185
230
const CTxMemPool& mempool = EnsureMemPool ();
186
231
187
- CHECK_NONFATAL (coinbase_script.size () == 1 );
188
-
189
- return generateBlocks (mempool, coinbase_script.at (0 ), num_blocks, max_tries);
232
+ return generateBlocks (mempool, coinbase_script, num_blocks, max_tries);
190
233
}
191
234
192
235
static UniValue generatetoaddress (const JSONRPCRequest& request)
@@ -229,6 +272,113 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
229
272
return generateBlocks (mempool, coinbase_script, nGenerate, nMaxTries);
230
273
}
231
274
275
+ static UniValue generateblock (const JSONRPCRequest& request)
276
+ {
277
+ RPCHelpMan{" generateblock" ,
278
+ " \n Mine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n " ,
279
+ {
280
+ {" address/descriptor" , RPCArg::Type::STR, RPCArg::Optional::NO, " The address or descriptor to send the newly generated bitcoin to." },
281
+ {" transactions" , RPCArg::Type::ARR, RPCArg::Optional::NO, " An array of hex strings which are either txids or raw transactions.\n "
282
+ " Txids must reference transactions currently in the mempool.\n "
283
+ " All transactions must be valid and in valid order, otherwise the block will be rejected." ,
284
+ {
285
+ {" rawtx/txid" , RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, " " },
286
+ },
287
+ }
288
+ },
289
+ RPCResult{
290
+ RPCResult::Type::OBJ, " " , " " ,
291
+ {
292
+ {RPCResult::Type::STR_HEX, " hash" , " hash of generated block" }
293
+ }
294
+ },
295
+ RPCExamples{
296
+ " \n Generate a block to myaddress, with txs rawtx and mempool_txid\n "
297
+ + HelpExampleCli (" generateblock" , R"( "myaddress" '["rawtx", "mempool_txid"]')" )
298
+ },
299
+ }.Check (request);
300
+
301
+ const auto address_or_descriptor = request.params [0 ].get_str ();
302
+ CScript coinbase_script;
303
+ std::string error;
304
+
305
+ if (!getScriptFromDescriptor (address_or_descriptor, coinbase_script, error)) {
306
+ const auto destination = DecodeDestination (address_or_descriptor);
307
+ if (!IsValidDestination (destination)) {
308
+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Error: Invalid address or descriptor" );
309
+ }
310
+
311
+ coinbase_script = GetScriptForDestination (destination);
312
+ }
313
+
314
+ const CTxMemPool& mempool = EnsureMemPool ();
315
+
316
+ std::vector<CTransactionRef> txs;
317
+ const auto raw_txs_or_txids = request.params [1 ].get_array ();
318
+ for (size_t i = 0 ; i < raw_txs_or_txids.size (); i++) {
319
+ const auto str (raw_txs_or_txids[i].get_str ());
320
+
321
+ uint256 hash;
322
+ CMutableTransaction mtx;
323
+ if (ParseHashStr (str, hash)) {
324
+
325
+ const auto tx = mempool.get (hash);
326
+ if (!tx) {
327
+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, strprintf (" Transaction %s not in mempool." , str));
328
+ }
329
+
330
+ txs.emplace_back (tx);
331
+
332
+ } else if (DecodeHexTx (mtx, str)) {
333
+ txs.push_back (MakeTransactionRef (std::move (mtx)));
334
+
335
+ } else {
336
+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, strprintf (" Transaction decode failed for %s" , str));
337
+ }
338
+ }
339
+
340
+ CChainParams chainparams (Params ());
341
+ CBlock block;
342
+
343
+ {
344
+ LOCK (cs_main);
345
+
346
+ CTxMemPool empty_mempool;
347
+ std::unique_ptr<CBlockTemplate> blocktemplate (BlockAssembler (empty_mempool, chainparams).CreateNewBlock (coinbase_script));
348
+ if (!blocktemplate) {
349
+ throw JSONRPCError (RPC_INTERNAL_ERROR, " Couldn't create new block" );
350
+ }
351
+ block = blocktemplate->block ;
352
+ }
353
+
354
+ CHECK_NONFATAL (block.vtx .size () == 1 );
355
+
356
+ // Add transactions
357
+ block.vtx .insert (block.vtx .end (), txs.begin (), txs.end ());
358
+ RegenerateCommitments (block);
359
+
360
+ {
361
+ LOCK (cs_main);
362
+
363
+ BlockValidationState state;
364
+ if (!TestBlockValidity (state, chainparams, block, LookupBlockIndex (block.hashPrevBlock ), false , false )) {
365
+ throw JSONRPCError (RPC_VERIFY_ERROR, strprintf (" TestBlockValidity failed: %s" , state.ToString ()));
366
+ }
367
+ }
368
+
369
+ uint256 block_hash;
370
+ uint64_t max_tries{1000000 };
371
+ unsigned int extra_nonce{0 };
372
+
373
+ if (!GenerateBlock (block, max_tries, extra_nonce, block_hash) || block_hash.IsNull ()) {
374
+ throw JSONRPCError (RPC_MISC_ERROR, " Failed to make block." );
375
+ }
376
+
377
+ UniValue obj (UniValue::VOBJ);
378
+ obj.pushKV (" hash" , block_hash.GetHex ());
379
+ return obj;
380
+ }
381
+
232
382
static UniValue getmininginfo (const JSONRPCRequest& request)
233
383
{
234
384
RPCHelpMan{" getmininginfo" ,
@@ -1038,6 +1188,7 @@ static const CRPCCommand commands[] =
1038
1188
1039
1189
{ " generating" , " generatetoaddress" , &generatetoaddress, {" nblocks" ," address" ," maxtries" } },
1040
1190
{ " generating" , " generatetodescriptor" , &generatetodescriptor, {" num_blocks" ," descriptor" ," maxtries" } },
1191
+ { " generating" , " generateblock" , &generateblock, {" address" ," transactions" } },
1041
1192
1042
1193
{ " util" , " estimatesmartfee" , &estimatesmartfee, {" conf_target" , " estimate_mode" } },
1043
1194
0 commit comments