@@ -101,6 +101,36 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
101101 return GetNetworkHashPS (!request.params [0 ].isNull () ? request.params [0 ].get_int () : 120 , !request.params [1 ].isNull () ? request.params [1 ].get_int () : -1 );
102102}
103103
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+
104134static UniValue generateBlocks (const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
105135{
106136 int nHeightEnd = 0 ;
@@ -119,29 +149,54 @@ static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbas
119149 if (!pblocktemplate.get ())
120150 throw JSONRPCError (RPC_INTERNAL_ERROR, " Couldn't create new block" );
121151 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)) {
131155 break ;
132156 }
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 ());
135161 }
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 ());
141162 }
142163 return blockHashes;
143164}
144165
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+
145200static UniValue generatetodescriptor (const JSONRPCRequest& request)
146201{
147202 RPCHelpMan{
@@ -166,27 +221,15 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
166221 const int num_blocks{request.params [0 ].get_int ()};
167222 const int64_t max_tries{request.params [2 ].isNull () ? 1000000 : request.params [2 ].get_int ()};
168223
169- FlatSigningProvider key_provider ;
224+ CScript coinbase_script ;
170225 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)) {
173227 throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, error);
174228 }
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- }
184229
185230 const CTxMemPool& mempool = EnsureMemPool ();
186231
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);
190233}
191234
192235static UniValue generatetoaddress (const JSONRPCRequest& request)
@@ -229,6 +272,113 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
229272 return generateBlocks (mempool, coinbase_script, nGenerate, nMaxTries);
230273}
231274
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+
232382static UniValue getmininginfo (const JSONRPCRequest& request)
233383{
234384 RPCHelpMan{" getmininginfo" ,
@@ -1038,6 +1188,7 @@ static const CRPCCommand commands[] =
10381188
10391189 { " generating" , " generatetoaddress" , &generatetoaddress, {" nblocks" ," address" ," maxtries" } },
10401190 { " generating" , " generatetodescriptor" , &generatetodescriptor, {" num_blocks" ," descriptor" ," maxtries" } },
1191+ { " generating" , " generateblock" , &generateblock, {" address" ," transactions" } },
10411192
10421193 { " util" , " estimatesmartfee" , &estimatesmartfee, {" conf_target" , " estimate_mode" } },
10431194
0 commit comments