mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
RPC support for signed blocks, compact blocks infrastructure
This commit is contained in:
parent
5877da8ed9
commit
8723debb3d
3 changed files with 462 additions and 12 deletions
|
|
@ -172,6 +172,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "rescanblockchain", 0, "start_height"},
|
||||
{ "rescanblockchain", 1, "stop_height"},
|
||||
{ "createwallet", 1, "disable_private_keys"},
|
||||
{ "combineblocksigs", 1, "signatures"},
|
||||
};
|
||||
|
||||
class CRPCConvertTable
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@
|
|||
#include <versionbitsinfo.h>
|
||||
#include <warnings.h>
|
||||
|
||||
#include <block_proof.h> // CheckProof
|
||||
#include <keystore.h> // combineblocksigs
|
||||
#include <script/generic.hpp> // combineblocksigs
|
||||
#include <blockencodings.h> // getcompactsketch
|
||||
#include <policy/policy.h> // testproposedblock
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
|
|
@ -127,15 +133,19 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
|
|||
LOCK(cs_main);
|
||||
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
|
||||
}
|
||||
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
|
||||
++pblock->nNonce;
|
||||
--nMaxTries;
|
||||
}
|
||||
if (nMaxTries == 0) {
|
||||
break;
|
||||
}
|
||||
if (pblock->nNonce == nInnerLoopCount) {
|
||||
continue;
|
||||
// Signed blocks have no PoW requirements, but merkle root computed above in
|
||||
// IncrementExtraNonce
|
||||
if (!g_signed_blocks) {
|
||||
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
|
||||
++pblock->nNonce;
|
||||
--nMaxTries;
|
||||
}
|
||||
if (nMaxTries == 0) {
|
||||
break;
|
||||
}
|
||||
if (pblock->nNonce == nInnerLoopCount) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
|
||||
if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr))
|
||||
|
|
@ -215,8 +225,10 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
|
|||
obj.pushKV("blocks", (int)chainActive.Height());
|
||||
obj.pushKV("currentblockweight", (uint64_t)nLastBlockWeight);
|
||||
obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx);
|
||||
obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
|
||||
obj.pushKV("networkhashps", getnetworkhashps(request));
|
||||
if (!g_signed_blocks) {
|
||||
obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
|
||||
obj.pushKV("networkhashps", getnetworkhashps(request));
|
||||
}
|
||||
obj.pushKV("pooledtx", (uint64_t)mempool.size());
|
||||
obj.pushKV("chain", Params().NetworkIDString());
|
||||
obj.pushKV("warnings", GetWarnings("statusbar"));
|
||||
|
|
@ -933,6 +945,360 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
|
|||
return result;
|
||||
}
|
||||
|
||||
UniValue getnewblockhex(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() > 1)
|
||||
throw std::runtime_error(
|
||||
"getnewblockhex\n"
|
||||
"\nGets hex representation of a proposed, unmined new block\n"
|
||||
"\nArguments:\n"
|
||||
"1. min_tx_age (numeric, optional, default=0) How many seconds a transaction must have been in the mempool to be inluded in the block proposal. This may help with faster block convergence among functionaries using compact blocks.\n"
|
||||
"\nResult\n"
|
||||
"blockhex (hex) The block hex\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getnewblockhex", "")
|
||||
);
|
||||
|
||||
int required_wait = !request.params[0].isNull() ? request.params[0].get_int() : 0;
|
||||
if (required_wait < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "min_tx_age must be non-negative.");
|
||||
}
|
||||
|
||||
CScript feeDestinationScript = Params().GetConsensus().mandatory_coinbase_destination;
|
||||
if (feeDestinationScript == CScript()) feeDestinationScript = CScript() << OP_TRUE;
|
||||
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(feeDestinationScript, true, required_wait));
|
||||
if (!pblocktemplate.get()) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty");
|
||||
}
|
||||
|
||||
{
|
||||
// IncrementExtraNonce sets coinbase flags and builds merkle tree
|
||||
LOCK(cs_main);
|
||||
unsigned int nExtraNonce = 0;
|
||||
IncrementExtraNonce(&pblocktemplate->block, chainActive.Tip(), nExtraNonce);
|
||||
}
|
||||
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock << pblocktemplate->block;
|
||||
return HexStr(ssBlock.begin(), ssBlock.end());
|
||||
}
|
||||
|
||||
UniValue combineblocksigs(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
throw std::runtime_error(
|
||||
"combineblocksigs \"blockhex\" [\"signature\",...]\n"
|
||||
"\nMerges signatures on a block proposal\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"blockhex\" (string, required) The hex-encoded block from getnewblockhex\n"
|
||||
"2. \"signatures\" (string) A json array of pubkey/signature pairs\n"
|
||||
" [\n"
|
||||
" {\n"
|
||||
" \"pubkey\":\"hex\", (string) The pubkey for the signature in hex\n"
|
||||
" \"sig\":\"hex\" (string) A signature (in the form of a hex-encoded scriptSig)\n"
|
||||
" ,...\n"
|
||||
" },\n"
|
||||
" ]\n"
|
||||
"\nResult\n"
|
||||
"{\n"
|
||||
" \"hex\": \"value\", (string) The signed block\n"
|
||||
" \"complete\": true|false (numeric) If block is complete \n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("combineblocksigs", "<hex> [\"signature1\", \"signature2\", ...]")
|
||||
);
|
||||
|
||||
if (!g_signed_blocks) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Signed blocks are not active for this network.");
|
||||
}
|
||||
|
||||
CBlock block;
|
||||
if (!DecodeHexBlk(block, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||
|
||||
const Consensus::Params& params = Params().GetConsensus();
|
||||
const UniValue& sigs = request.params[1].get_array();
|
||||
CBasicKeyStore keystore;
|
||||
SignatureData sig_data;
|
||||
SimpleSignatureCreator signature_creator(block.GetHash());
|
||||
for (unsigned int i = 0; i < sigs.size(); i++) {
|
||||
UniValue pubkey_sig = sigs[i];
|
||||
const std::string& pubkey_str = pubkey_sig["pubkey"].get_str();
|
||||
const std::string& sig_str = pubkey_sig["sig"].get_str();
|
||||
if (!IsHex(sig_str) || !IsHex(pubkey_str)) {
|
||||
continue;
|
||||
}
|
||||
std::vector<unsigned char> pubkey_bytes = ParseHex(pubkey_str);
|
||||
std::vector<unsigned char> sig_bytes = ParseHex(sig_str);
|
||||
CPubKey pubkey(pubkey_bytes.begin(), pubkey_bytes.end());
|
||||
if (!pubkey.IsFullyValid()) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Bad pubkey");
|
||||
}
|
||||
sig_data.signatures[pubkey.GetID()] = std::make_pair(pubkey, sig_bytes);
|
||||
}
|
||||
|
||||
// Finalizes the signatures, has no access to keys
|
||||
ProduceSignature(keystore, signature_creator, block.proof.challenge, sig_data, SCRIPT_NO_SIGHASH_BYTE);
|
||||
block.proof.solution = sig_data.scriptSig;
|
||||
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
|
||||
ssBlock << block;
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("hex", HexStr(ssBlock.begin(), ssBlock.end()));
|
||||
result.pushKV("complete", CheckProof(block, params));
|
||||
return result;
|
||||
}
|
||||
|
||||
UniValue getcompactsketch(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error(
|
||||
"getcompactsketch block_hex\n"
|
||||
"\nGets hex representation of a proposed compact block sketch.\n"
|
||||
"It is consumed by `consumecompactsketch.`\n"
|
||||
"Arguments:\n"
|
||||
"1. \"block_hex\" (string, required), Hex serialized block proposal from `getnewblockhex`.\n"
|
||||
"\nResult\n"
|
||||
"sketch (string) The block serialized block sketch in hex\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getcompactsketch", "")
|
||||
);
|
||||
|
||||
CBlock block;
|
||||
std::vector<unsigned char> block_bytes(ParseHex(request.params[0].get_str()));
|
||||
CDataStream ssBlock(block_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock >> block;
|
||||
|
||||
CBlockHeaderAndShortTxIDs cmpctblock(block, true);
|
||||
|
||||
CDataStream ssCompactBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssCompactBlock << cmpctblock;
|
||||
return HexStr(ssCompactBlock.begin(), ssCompactBlock.end());
|
||||
|
||||
}
|
||||
|
||||
UniValue consumecompactsketch(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error(
|
||||
"consumecompactsketch sketch\n"
|
||||
"\nTakes hex representation of a proposed compact block sketch and fills it in\n"
|
||||
"using mempool. Returns the block if complete, and a list\n"
|
||||
"of missing transaction indices serialized as a native structure."
|
||||
"NOTE: The latest instance of this call will have a partially filled block\n"
|
||||
"cached in memory to be used in `consumegetblocktxn` to finalize the block.\n"
|
||||
"Arguments:\n"
|
||||
"1. \"sketch\" (string, required), Hex string of compact block sketch.\n"
|
||||
"\nResult\n"
|
||||
"{\n"
|
||||
" blockhex (hex) The filled block hex. Only returns when block is final\n"
|
||||
" block_tx_req (hex) The serialized structure of missing transaction indices, given to serving node\n"
|
||||
" found_transactions (hex) The serialized list of found transactions to be used in finalizecompactblock\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("consumecompactsketch", "<sketch>")
|
||||
);
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
std::vector<unsigned char> compact_block_bytes(ParseHex(request.params[0].get_str()));
|
||||
CDataStream ssBlock(compact_block_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
CBlockHeaderAndShortTxIDs cmpctblock;
|
||||
ssBlock >> cmpctblock;
|
||||
|
||||
PartiallyDownloadedBlock partialBlock(&mempool);
|
||||
const std::vector<std::pair<uint256, CTransactionRef>> dummy;
|
||||
ReadStatus status = partialBlock.InitData(cmpctblock, dummy);
|
||||
if (status != READ_STATUS_OK) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Compact block decode failed");
|
||||
}
|
||||
|
||||
BlockTransactionsRequest req;
|
||||
std::vector<CTransactionRef> found(partialBlock.GetAvailableTx());
|
||||
for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) {
|
||||
if (!partialBlock.IsTxAvailable(i)) {
|
||||
req.indexes.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
CDataStream ssReq(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssReq << req;
|
||||
|
||||
if (req.indexes.empty()) {
|
||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
|
||||
std::vector<CTransactionRef> dummy;
|
||||
ReadStatus status = partialBlock.FillBlock(*pblock, dummy, false /* don't get pow */);
|
||||
if (status == READ_STATUS_INVALID) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Bogus crap sketch.");
|
||||
} else if (status == READ_STATUS_FAILED) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Failed to complete block though all transactions were apparently found. Could be random short ID collision; requires full block instead.");
|
||||
} else if (status == READ_STATUS_CHECKBLOCK_FAILED) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Checkblock failed.");
|
||||
}
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock << *pblock;
|
||||
|
||||
ret.pushKV("blockhex", HexStr(ssBlock.begin(), ssBlock.end()));
|
||||
} else {
|
||||
// Serialize the list of transactions we found
|
||||
CDataStream ssFound(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssFound << found;
|
||||
|
||||
ret.pushKV("block_tx_req", HexStr(ssReq.begin(), ssReq.end()));
|
||||
ret.pushKV("found_transactions", HexStr(ssFound.begin(), ssFound.end()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue consumegetblocktxn(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
throw std::runtime_error(
|
||||
"consumegetblocktxn full_block block_tx_req\n"
|
||||
"Consumes a transaction request for a compact block sketch."
|
||||
"Arguments:\n"
|
||||
"1. \"full_block\" (string, required), Hex serialied block that corresponds to the block request `block_tx_req`.\n"
|
||||
"2. \"block_tx_req\" (string, required), Hex serialied BlockTransactionsRequest, aka getblocktxn network message.\n"
|
||||
"\nResult\n"
|
||||
"block_transactions (hex) The serialized list of found transactions aka BlockTransactions\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("consumegetblocktxn", "<block_tx_req>")
|
||||
);
|
||||
|
||||
|
||||
CBlock block;
|
||||
std::vector<unsigned char> block_bytes(ParseHex(request.params[0].get_str()));
|
||||
CDataStream ssBlock(block_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock >> block;
|
||||
|
||||
// Take in BlockTransactionsRequest, return BlockTransactions
|
||||
std::vector<unsigned char> block_req(ParseHex(request.params[1].get_str()));
|
||||
CDataStream ssReq(block_req, SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
BlockTransactionsRequest req;
|
||||
ssReq >> req;
|
||||
|
||||
BlockTransactions resp(req);
|
||||
for (size_t i = 0; i < req.indexes.size(); i++) {
|
||||
if (req.indexes[i] >= block.vtx.size()) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Peer sent us a getblocktxn with out-of-bounds tx indices");
|
||||
}
|
||||
resp.txn[i] = block.vtx[req.indexes[i]];
|
||||
}
|
||||
|
||||
CDataStream ssResp(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssResp << resp;
|
||||
|
||||
return HexStr(ssResp.begin(), ssResp.end());
|
||||
}
|
||||
|
||||
UniValue finalizecompactblock(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 3)
|
||||
throw std::runtime_error(
|
||||
"finalizecompactblock compact_hex block_transactions found_transactions\n"
|
||||
"Takes the two transaction lists, fills out the compact block and attempts to finalize it."
|
||||
"Arguments:\n"
|
||||
"1. \"compact_hex\" (string, required), Hex serialized compact block.\n"
|
||||
"2. \"block_transactions\" (string, required), Hex serialized BlockTransactions, the response to getblocktxn.\n"
|
||||
"3. \"found_transactions\" (string, required), Hex serialized list of transactions that were found in response to receiving a compact sketch in `consumecompactsketch`.\n"
|
||||
"\nResult\n"
|
||||
"block (hex) The serialized final block.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("finalizecompactblock", "<compact_hex> <block_transactions> <found_transactions>")
|
||||
);
|
||||
|
||||
// Compact block
|
||||
std::vector<unsigned char> compact_block_bytes(ParseHex(request.params[0].get_str()));
|
||||
CDataStream ssCompactBlock(compact_block_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
CBlockHeaderAndShortTxIDs cmpctblock;
|
||||
ssCompactBlock >> cmpctblock;
|
||||
|
||||
// BlockTransactions from the server
|
||||
std::vector<unsigned char> block_tx(ParseHex(request.params[1].get_str()));
|
||||
CDataStream ssResp(block_tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
BlockTransactions transactions;
|
||||
ssResp >> transactions;
|
||||
|
||||
// Cached transactions
|
||||
std::vector<unsigned char> found_tx(ParseHex(request.params[2].get_str()));
|
||||
CDataStream ssFound(block_tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
std::vector<CTransactionRef> found;
|
||||
ssFound >> found;
|
||||
|
||||
// Make mega-list
|
||||
found.insert(found.end(), transactions.txn.begin(), transactions.txn.end());
|
||||
|
||||
// Now construct the final block!
|
||||
PartiallyDownloadedBlock partialBlock(&mempool);
|
||||
|
||||
const std::vector<std::pair<uint256, CTransactionRef>> dummy;
|
||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
|
||||
if (partialBlock.InitData(cmpctblock, dummy) != READ_STATUS_OK || partialBlock.FillBlock(*pblock, found, false /* pow_check*/) != READ_STATUS_OK) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Failed to complete block though all transactions were apparently found. Could be random short ID collision; requires full block instead.");
|
||||
}
|
||||
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock << *pblock;
|
||||
|
||||
return HexStr(ssBlock.begin(), ssBlock.end());
|
||||
}
|
||||
|
||||
UniValue testproposedblock(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
||||
throw std::runtime_error(
|
||||
"testproposedblock \"blockhex\"\n"
|
||||
"\nChecks a block proposal for validity, and that it extends chaintip\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"blockhex\" (string, required) The hex-encoded block from getnewblockhex\n"
|
||||
"2. \"acceptnonstd\" (bool, optional) If set false, returns error if block contains non-standard transaction. Default is set via `-acceptnonstdtxn`\n"
|
||||
"\nResult\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("testproposedblock", "<hex>")
|
||||
);
|
||||
|
||||
CBlock block;
|
||||
if (!DecodeHexBlk(block, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hash);
|
||||
if (mi != mapBlockIndex.end())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, "already have block");
|
||||
|
||||
CBlockIndex* const pindexPrev = chainActive.Tip();
|
||||
// TestBlockValidity only supports blocks built on the current Tip
|
||||
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, "proposal was not based on our best chain");
|
||||
|
||||
CValidationState state;
|
||||
if (!TestBlockValidity(state, Params(), block, pindexPrev, false, true) || !state.IsValid()) {
|
||||
std::string strRejectReason = state.GetRejectReason();
|
||||
if (strRejectReason.empty())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, state.IsInvalid() ? "Block proposal was invalid" : "Error checking block proposal");
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
|
||||
}
|
||||
|
||||
const CChainParams& chainparams = Params();
|
||||
const bool acceptnonstd = !request.params[1].isNull() ? request.params[1].get_bool() : gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
|
||||
if (!acceptnonstd) {
|
||||
for (auto& transaction : block.vtx) {
|
||||
if (transaction->IsCoinBase()) continue;
|
||||
std::string reason;
|
||||
if (!IsStandardTx(*transaction, reason)) {
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, "Block proposal included a non-standard transaction: " + reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
static const CRPCCommand commands[] =
|
||||
{ // category name actor (function) argNames
|
||||
// --------------------- ------------------------ ----------------------- ----------
|
||||
|
|
@ -940,9 +1306,17 @@ static const CRPCCommand commands[] =
|
|||
{ "mining", "getmininginfo", &getmininginfo, {} },
|
||||
{ "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} },
|
||||
{ "mining", "getblocktemplate", &getblocktemplate, {"template_request"} },
|
||||
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} },
|
||||
{ "generating", "combineblocksigs", &combineblocksigs, {"blockhex","signatures"} },
|
||||
{ "generating", "getnewblockhex", &getnewblockhex, {"min_tx_age"} },
|
||||
{ "generating", "getcompactsketch", &getcompactsketch, {"block_hex"} },
|
||||
{ "generating", "consumecompactsketch", &consumecompactsketch, {"sketch"} },
|
||||
{ "generating", "consumegetblocktxn", &consumegetblocktxn, {"full_block", "block_tx_req"} },
|
||||
{ "generating", "finalizecompactblock", &finalizecompactblock, {"compact_hex","block_transactions","found_transactions"} },
|
||||
{ "mining", "testproposedblock", &testproposedblock, {"blockhex", "acceptnonstd"} },
|
||||
|
||||
|
||||
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} },
|
||||
|
||||
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },
|
||||
|
||||
{ "hidden", "estimatefee", &estimatefee, {} },
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
#include <functional>
|
||||
|
||||
#include <script/generic.hpp> // signblock
|
||||
|
||||
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
||||
|
||||
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
|
||||
|
|
@ -3879,6 +3881,78 @@ static UniValue bumpfee(const JSONRPCRequest& request)
|
|||
return result;
|
||||
}
|
||||
|
||||
UniValue signblock(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error(
|
||||
"signblock \"blockhex\"\n"
|
||||
"\nSigns a block proposal, checking that it would be accepted first. Errors if it cannot sign the block.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"blockhex\" (string, required) The hex-encoded block from getnewblockhex\n"
|
||||
"\nResult\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
" pubkeys, (hex) The signature's pubkey\n"
|
||||
" sig (hex) The signature script\n"
|
||||
" },\n"
|
||||
" ...\n"
|
||||
"]\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("signblock", "0000002018c6f2f913f9902aeab...5ca501f77be96de63f609010000000000000000015100000000")
|
||||
);
|
||||
|
||||
if (!g_signed_blocks) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Signed blocks are not active for this network.");
|
||||
}
|
||||
|
||||
CBlock block;
|
||||
if (!DecodeHexBlk(block, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hash);
|
||||
if (mi != mapBlockIndex.end())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, "already have block");
|
||||
|
||||
CBlockIndex* const pindexPrev = chainActive.Tip();
|
||||
// TestBlockValidity only supports blocks built on the current Tip
|
||||
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, "proposal was not based on our best chain");
|
||||
|
||||
CValidationState state;
|
||||
if (!TestBlockValidity(state, Params(), block, pindexPrev, false, true) || !state.IsValid()) {
|
||||
std::string strRejectReason = state.GetRejectReason();
|
||||
if (strRejectReason.empty())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, state.IsInvalid() ? "Block proposal was invalid" : "Error checking block proposal");
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
|
||||
}
|
||||
|
||||
// Expose SignatureData internals in return value in lieu of "Partially Signed Bitcoin Blocks"
|
||||
SignatureData block_sigs;
|
||||
GenericSignScript(*pwallet, block.GetBlockHeader(), block.proof.challenge, block_sigs);
|
||||
|
||||
// Error if sig data didn't "grow"
|
||||
if (!block_sigs.complete && block_sigs.signatures.empty()) {
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, "Could not sign the block.");
|
||||
}
|
||||
UniValue ret(UniValue::VARR);
|
||||
for (const auto& signature : block_sigs.signatures) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("pubkey", HexStr(signature.second.first.begin(), signature.second.first.end()));
|
||||
obj.pushKV("sig", HexStr(signature.second.second.begin(), signature.second.second.end()));
|
||||
ret.push_back(obj);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue generate(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
|
|
@ -4838,6 +4912,7 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "setlabel", &setlabel, {"address","label"} },
|
||||
|
||||
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
|
||||
{ "wallet", "signblock", &signblock, {"blockhex"}},
|
||||
};
|
||||
|
||||
void RegisterWalletRPCCommands(CRPCTable &t)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue