From c8ac3bf7d775b7d3ad7f180fa3a70b8b1f5649f8 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 10 Apr 2015 17:50:43 +0100 Subject: [PATCH] Add block sign/sig combine/etc RPC functions --- src/core_io.h | 1 + src/core_write.cpp | 7 ++++ src/rpc/client.cpp | 2 +- src/rpc/mining.cpp | 79 ++++++++++++++++++++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 48 ++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) diff --git a/src/core_io.h b/src/core_io.h index b559d44bf5..c796a936d5 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -27,6 +27,7 @@ extern std::vector ParseHexUV(const UniValue& v, const std::strin extern std::string FormatScript(const CScript& script); extern std::string EncodeHexTx(const CTransaction& tx); extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); +extern std::string EncodeHexBlock(const CBlock& block); extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry); #endif // BITCOIN_CORE_IO_H diff --git a/src/core_write.cpp b/src/core_write.cpp index ea01ddc10d..8a548bd659 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -123,6 +123,13 @@ string EncodeHexTx(const CTransaction& tx) return HexStr(ssTx.begin(), ssTx.end()); } +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 b88c58e58c..82a9b459c3 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -28,7 +28,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "setmocktime", 0 }, { "getaddednodeinfo", 0 }, { "generate", 0 }, - { "generate", 1 }, + { "combineblocksigs", 1 }, { "getnetworkhashps", 0 }, { "getnetworkhashps", 1 }, { "sendtoaddress", 1 }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index ecd6a295c0..60067c5e37 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -100,6 +100,83 @@ UniValue generate(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method cannot be used in private chain mode"); } +UniValue getnewblockhex(const UniValue& params, bool fHelp) +{ + if (fHelp || 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", "") + ); + + CScript coinbaseDest(Params().CoinbaseDestination()); + if (coinbaseDest == CScript()) + coinbaseDest = CScript() << OP_TRUE; + + std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseDest)); + 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 UniValue& params, bool fHelp) +{ + if (fHelp || 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, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + + UniValue result(UniValue::VOBJ); + const UniValue& sigs = 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 UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -800,6 +877,8 @@ static const CRPCCommand commands[] = { "mining", "getblocktemplate", &getblocktemplate, true }, { "mining", "submitblock", &submitblock, true }, + { "generating", "combineblocksigs", &combineblocksigs, true }, + { "generating", "getnewblockhex", &getnewblockhex, true }, { "generating", "generate", &generate, true }, { "util", "estimatefee", &estimatefee, true }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2ad379e46b..92eaaad23f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -7,6 +7,7 @@ #include "base58.h" #include "chain.h" #include "core_io.h" +#include "consensus/validation.h" #include "init.h" #include "main.h" #include "net.h" @@ -2567,6 +2568,52 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) return result; } +UniValue signblock(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || 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, 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 UniValue& params, bool fHelp); // in rpcdump.cpp extern UniValue importprivkey(const UniValue& params, bool fHelp); extern UniValue importaddress(const UniValue& params, bool fHelp); @@ -2620,6 +2667,7 @@ static const CRPCCommand commands[] = { "wallet", "sendtoaddress", &sendtoaddress, false }, { "wallet", "setaccount", &setaccount, true }, { "wallet", "settxfee", &settxfee, true }, + { "wallet", "signblock", &signblock, true }, { "wallet", "signmessage", &signmessage, true }, { "wallet", "walletlock", &walletlock, true }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, true },