@@ -100,6 +100,36 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
100
100
return GetNetworkHashPS (!request.params [0 ].isNull () ? request.params [0 ].get_int () : 120 , !request.params [1 ].isNull () ? request.params [1 ].get_int () : -1 );
101
101
}
102
102
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
+
103
133
static UniValue generateBlocks (const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
104
134
{
105
135
int nHeightEnd = 0 ;
@@ -118,29 +148,54 @@ static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbas
118
148
if (!pblocktemplate.get ())
119
149
throw JSONRPCError (RPC_INTERNAL_ERROR, " Couldn't create new block" );
120
150
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)) {
130
154
break ;
131
155
}
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 ());
134
160
}
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 ());
140
161
}
141
162
return blockHashes;
142
163
}
143
164
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
+
144
199
static UniValue generatetodescriptor (const JSONRPCRequest& request)
145
200
{
146
201
RPCHelpMan{
@@ -165,27 +220,15 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
165
220
const int num_blocks{request.params [0 ].get_int ()};
166
221
const int64_t max_tries{request.params [2 ].isNull () ? 1000000 : request.params [2 ].get_int ()};
167
222
168
- FlatSigningProvider key_provider ;
223
+ CScript coinbase_script ;
169
224
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)) {
172
226
throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, error);
173
227
}
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
- }
183
228
184
229
const CTxMemPool& mempool = EnsureMemPool ();
185
230
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);
189
232
}
190
233
191
234
static UniValue generatetoaddress (const JSONRPCRequest& request)
@@ -228,6 +271,113 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
228
271
return generateBlocks (mempool, coinbase_script, nGenerate, nMaxTries);
229
272
}
230
273
274
+ static UniValue generateblock (const JSONRPCRequest& request)
275
+ {
276
+ RPCHelpMan{" generateblock" ,
277
+ " \n Mine 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
+ " \n Generate 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
+
231
381
static UniValue getmininginfo (const JSONRPCRequest& request)
232
382
{
233
383
RPCHelpMan{" getmininginfo" ,
@@ -1035,6 +1185,7 @@ static const CRPCCommand commands[] =
1035
1185
1036
1186
{ " generating" , " generatetoaddress" , &generatetoaddress, {" nblocks" ," address" ," maxtries" } },
1037
1187
{ " generating" , " generatetodescriptor" , &generatetodescriptor, {" num_blocks" ," descriptor" ," maxtries" } },
1188
+ { " generating" , " generateblock" , &generateblock, {" address" ," transactions" } },
1038
1189
1039
1190
{ " util" , " estimatesmartfee" , &estimatesmartfee, {" conf_target" , " estimate_mode" } },
1040
1191
0 commit comments