@@ -220,6 +220,155 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
220220 return generateBlocks (coinbase_script, nGenerate, nMaxTries);
221221}
222222
223+ static std::string GenerateCustomBlock (const CScript& coinbase_script, const std::vector<CTransactionRef>& txs)
224+ {
225+ CBlock block;
226+ CChainParams chainparams (Params ());
227+
228+ CBlockIndex* previous_index;
229+ {
230+ LOCK (cs_main);
231+ previous_index = ::ChainActive ().Tip ();
232+ }
233+ CHECK_NONFATAL (previous_index != nullptr );
234+
235+ const int height = previous_index->nHeight + 1 ;
236+
237+ // Create coinbase transaction.
238+ CMutableTransaction coinbase_tx;
239+ coinbase_tx.vin .resize (1 );
240+ coinbase_tx.vin [0 ].prevout .SetNull ();
241+ coinbase_tx.vout .resize (1 );
242+ coinbase_tx.vout [0 ].scriptPubKey = coinbase_script;
243+ coinbase_tx.vout [0 ].nValue = GetBlockSubsidy (height, chainparams.GetConsensus ());
244+ coinbase_tx.vin [0 ].scriptSig = CScript () << height << OP_0;
245+ block.vtx .push_back (MakeTransactionRef (std::move (coinbase_tx)));
246+
247+ // Add transactions
248+ block.vtx .insert (block.vtx .end (), txs.begin (), txs.end ());
249+
250+ block.nVersion = ComputeBlockVersion (previous_index, chainparams.GetConsensus ());
251+ if (chainparams.MineBlocksOnDemand ())
252+ block.nVersion = gArgs .GetArg (" -blockversion" , block.nVersion );
253+
254+ // Fill in header
255+ block.hashPrevBlock = previous_index->GetBlockHash ();
256+ block.nTime = GetAdjustedTime ();
257+ UpdateTime (&block, chainparams.GetConsensus (), previous_index);
258+ block.nBits = GetNextWorkRequired (previous_index, &block, chainparams.GetConsensus ());
259+ block.nNonce = 0 ;
260+
261+ GenerateCoinbaseCommitment (block, previous_index, chainparams.GetConsensus ());
262+
263+ {
264+ LOCK (cs_main);
265+ unsigned int extra_nonce = 0 ;
266+ IncrementExtraNonce (&block, ::ChainActive ().Tip (), extra_nonce);
267+
268+ BlockValidationState state;
269+ if (!TestBlockValidity (state, chainparams, block, previous_index, false , false )) {
270+ throw JSONRPCError (RPC_VERIFY_ERROR, strprintf (" TestBlockValidity failed: %s" , FormatStateMessage (state)));
271+ }
272+ }
273+
274+ int max_tries{1000000 };
275+
276+ while (max_tries > 0 && !CheckProofOfWork (block.GetHash (), block.nBits , chainparams.GetConsensus ()) && !ShutdownRequested ()) {
277+ ++block.nNonce ;
278+ --max_tries;
279+ }
280+
281+ if (max_tries == 0 ) {
282+ throw JSONRPCError (RPC_INTERNAL_ERROR, " Exceeded max tries" );
283+ }
284+
285+ std::shared_ptr<const CBlock> shared_block = std::make_shared<const CBlock>(block);
286+ if (!ProcessNewBlock (chainparams, shared_block, true , nullptr ))
287+ throw JSONRPCError (RPC_INTERNAL_ERROR, " ProcessNewBlock, block not accepted" );
288+
289+ return block.GetHash ().GetHex ();
290+ }
291+
292+ static UniValue generatecustomblock (const JSONRPCRequest& request)
293+ {
294+ RPCHelpMan{" generatecustomblock" ,
295+ " \n Mine a custom block with a set of transactions immediately to a specified address or descriptor (before the RPC call returns)\n " ,
296+ {
297+ {" address/descriptor" , RPCArg::Type::STR, RPCArg::Optional::NO, " The address or descriptor to send the newly generated bitcoin to." },
298+ {" transactions" , RPCArg::Type::ARR, RPCArg::Optional::NO, " An array of hex strings which are either txids or raw transactions.\n "
299+ " Txids must reference transactions currently in the mempool." ,
300+ {
301+ {" rawtx/txid" , RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, " " },
302+ },
303+ }
304+ },
305+ RPCResult{
306+ " blockhash (hex) hash of generated block\n "
307+ },
308+ RPCExamples{
309+ " \n Generate a block to myaddress, with txs rawtx and mempool_txid\n "
310+ + HelpExampleCli (" generatecustomblock" , R"( "myaddress" '["rawtx", "mempool_txid"]')" )
311+ },
312+ }.Check (request);
313+
314+ const auto address_or_descriptor = request.params [0 ].get_str ();
315+ CScript coinbase_script;
316+
317+ FlatSigningProvider key_provider;
318+ std::string error;
319+ const auto desc = Parse (address_or_descriptor, key_provider, error, /* require_checksum = */ false );
320+ if (desc) {
321+ if (desc->IsRange ()) {
322+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Ranged descriptor not accepted. Maybe pass through deriveaddresses first?" );
323+ }
324+
325+ FlatSigningProvider provider;
326+ std::vector<CScript> scripts;
327+ if (!desc->Expand (0 , key_provider, scripts, provider)) {
328+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, strprintf (" Cannot derive script without private keys" ));
329+ }
330+
331+ CHECK_NONFATAL (scripts.size () == 1 );
332+ coinbase_script = scripts.at (0 );
333+
334+ } else {
335+ const auto destination = DecodeDestination (address_or_descriptor);
336+ if (!IsValidDestination (destination)) {
337+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Error: Invalid address or descriptor" );
338+ }
339+
340+ coinbase_script = GetScriptForDestination (destination);
341+ }
342+
343+ std::vector<CTransactionRef> txs;
344+ const auto raw_txs_or_txids = request.params [1 ].get_array ();
345+ for (size_t i = 0 ; i < raw_txs_or_txids.size (); i++) {
346+ const auto str (raw_txs_or_txids[i].get_str ());
347+
348+ uint256 hash;
349+ CMutableTransaction mtx;
350+ if (ParseHashStr (str, hash)) {
351+
352+ LOCK (mempool.cs );
353+
354+ const auto it = mempool.mapTx .find (hash);
355+ if (it == mempool.mapTx .end ()) {
356+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, strprintf (" Transaction %s not in mempool." , str));
357+ }
358+
359+ txs.emplace_back (it->GetSharedTx ());
360+
361+ } else if (DecodeHexTx (mtx, str)) {
362+ txs.push_back (MakeTransactionRef (std::move (mtx)));
363+
364+ } else {
365+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, strprintf (" Transaction decode failed for %s" , str));
366+ }
367+ }
368+
369+ return GenerateCustomBlock (coinbase_script, txs);
370+ }
371+
223372static UniValue getmininginfo (const JSONRPCRequest& request)
224373{
225374 RPCHelpMan{" getmininginfo" ,
@@ -1003,6 +1152,7 @@ static const CRPCCommand commands[] =
10031152
10041153 { " generating" , " generatetoaddress" , &generatetoaddress, {" nblocks" ," address" ," maxtries" } },
10051154 { " generating" , " generatetodescriptor" , &generatetodescriptor, {" num_blocks" ," descriptor" ," maxtries" } },
1155+ { " generating" , " generatecustomblock" , &generatecustomblock, {" address" ," transactions" } },
10061156
10071157 { " util" , " estimatesmartfee" , &estimatesmartfee, {" conf_target" , " estimate_mode" } },
10081158
0 commit comments