diff --git a/src/core_io.h b/src/core_io.h index ee323a22eed51..d53a45c0cb80d 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -11,6 +11,7 @@ #include class CBlock; +class CBlockHeader; class CScript; class CTransaction; struct CMutableTransaction; @@ -23,6 +24,7 @@ CScript ParseScript(const std::string& s); std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true); bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); +bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header); uint256 ParseHashStr(const std::string&, const std::string& strName); std::vector ParseHexUV(const UniValue& v, const std::string& strName); bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error); diff --git a/src/core_read.cpp b/src/core_read.cpp index a5df45aba194f..b02016c014da1 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -145,6 +145,20 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no return false; } +bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header) +{ + if (!IsHex(hex_header)) return false; + + const std::vector header_data{ParseHex(hex_header)}; + CDataStream ser_header(header_data, SER_NETWORK, PROTOCOL_VERSION); + try { + ser_header >> header; + } catch (const std::exception&) { + return false; + } + return true; +} + bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) { if (!IsHex(strHexBlk)) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index e751587dc71c0..623b0bd86aaca 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include #include +#include #include #include @@ -763,6 +763,42 @@ static UniValue submitblock(const JSONRPCRequest& request) return BIP22ValidationResult(sc.state); } +static UniValue submitheader(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) { + throw std::runtime_error( + "submitheader \"hexdata\"\n" + "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid." + "\nThrows when the header is invalid.\n" + "\nArguments\n" + "1. \"hexdata\" (string, required) the hex-encoded block header data\n" + "\nResult:\n" + "None" + "\nExamples:\n" + + HelpExampleCli("submitheader", "\"aabbcc\"") + + HelpExampleRpc("submitheader", "\"aabbcc\"")); + } + + CBlockHeader h; + if (!DecodeHexBlockHeader(h, request.params[0].get_str())) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed"); + } + { + LOCK(cs_main); + if (!LookupBlockIndex(h.hashPrevBlock)) { + throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first"); + } + } + + CValidationState state; + ProcessNewBlockHeaders({h}, state, Params(), /* ppindex */ nullptr, /* first_invalid */ nullptr); + if (state.IsValid()) return NullUniValue; + if (state.IsError()) { + throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); + } + throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); +} + static UniValue estimatefee(const JSONRPCRequest& request) { throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n" @@ -940,6 +976,7 @@ static const CRPCCommand commands[] = { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} }, { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, + { "mining", "submitheader", &submitheader, {"hexdata"} }, { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },