diff --git a/src/core_io.h b/src/core_io.h index 2d63be5fc4..44c23101b0 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -29,5 +29,6 @@ std::string FormatScript(const CScript& script); std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0); void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry); +extern std::string EncodeHexBlock(const CBlock& block); #endif // BITCOIN_CORE_IO_H diff --git a/src/core_write.cpp b/src/core_write.cpp index b0993a131f..e431b9f144 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -121,6 +121,13 @@ std::string EncodeHexTx(const CTransaction& tx, const int serialFlags) return HexStr(ssTx.begin(), ssTx.end()); } +std::string EncodeHexBlock(const CBlock& block) +{ + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + return HexStr(ssBlock.begin(), ssBlock.end()); +} + void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 56d1495919..2c767e9869 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -35,7 +35,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "generate", 0, "nblocks" }, { "generate", 1, "maxtries" }, { "generatetoaddress", 0, "nblocks" }, - { "generatetoaddress", 2, "maxtries" }, + { "combineblocksigs", 1, "signatures"}, { "getnetworkhashps", 0, "nblocks" }, { "getnetworkhashps", 1, "height" }, { "sendtoaddress", 1, "amount" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9898bb0801..55e7de57ff 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -101,6 +101,79 @@ UniValue generate(const JSONRPCRequest& request) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method cannot be used in private chain mode"); } +UniValue getnewblockhex(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "getnewblockhex\n" + "\nGets hex representation of a proposed, unmined new block\n" + "\nResult\n" + "blockhex (hex) The block hex\n" + "\nExamples:\n" + + HelpExampleCli("getnewblockhex", "") + ); + + std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(CScript() << OP_TRUE)); + 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 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 signatures\n" + " [\n" + " \"signature\" (string) A signature (in the form of a hex-encoded scriptSig)\n" + " ,...\n" + " ]\n" + "\nResult\n" + "{\n" + " \"hex\": \"value\", (string) The signed block\n" + " \"complete\": n (numeric) if block is complete \n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("combineblocksigs", "") + ); + + CBlock block; + if (!DecodeHexBlk(block, request.params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + + UniValue result(UniValue::VOBJ); + const UniValue& sigs = request.params[1].get_array(); + for (unsigned int i = 0; i < sigs.size(); i++) { + const std::string& sig = sigs[i].get_str(); + if (!IsHex(sig)) + continue; + std::vector vchScript = ParseHex(sig); + block.proof.solution = CombineBlockSignatures(block, block.proof.solution, CScript(vchScript.begin(), vchScript.end())); + if (CheckProof(block, Params().GetConsensus())) { + result.push_back(Pair("hex", EncodeHexBlock(block))); + result.push_back(Pair("complete", true)); + return result; + } + } + + result.push_back(Pair("hex", EncodeHexBlock(block))); + result.push_back(Pair("complete", false)); + return result; +} + UniValue getmininginfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) @@ -819,6 +892,8 @@ static const CRPCCommand commands[] = { "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} }, { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, + { "generating", "combineblocksigs", &combineblocksigs, true, {} }, + { "generating", "getnewblockhex", &getnewblockhex, true, {} }, { "util", "estimatefee", &estimatefee, true, {"nblocks"} }, { "util", "estimatepriority", &estimatepriority, true, {"nblocks"} }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5cf1592376..92fdef5bcd 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8,6 +8,7 @@ #include "chain.h" #include "consensus/validation.h" #include "core_io.h" +#include "consensus/validation.h" #include "init.h" #include "validation.h" #include "net.h" @@ -2984,6 +2985,52 @@ UniValue bumpfee(const JSONRPCRequest& request) return result; } +UniValue signblock(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "signblock \"blockhex\"\n" + "\nSigns a block proposal, checking that it would be accepted first\n" + "\nArguments:\n" + "1. \"blockhex\" (string, required) The hex-encoded block from getnewblockhex\n" + "\nResult\n" + " sig (hex) The signature\n" + "\nExamples:\n" + + HelpExampleCli("getnewblockhex", "") + ); + + 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); + } + + block.proof.solution = CScript(); + MaybeGenerateProof(&block, pwalletMain); + return HexStr(block.proof.solution.begin(), block.proof.solution.end()); +} + extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); extern UniValue importaddress(const JSONRPCRequest& request); @@ -3039,6 +3086,7 @@ static const CRPCCommand commands[] = { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} }, { "wallet", "setaccount", &setaccount, true, {"address","account"} }, + { "wallet", "signblock", &signblock, true, {} }, { "wallet", "settxfee", &settxfee, true, {"amount"} }, { "wallet", "signmessage", &signmessage, true, {"address","message"} }, { "wallet", "walletlock", &walletlock, true, {} },