From 3390014fd0d91b0148425e794ac01c10b646a682 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 12 May 2012 14:31:32 +0000 Subject: [PATCH] Minimal BIP 22 (getblocktemplate) support - Replaces getmemorypool with new getblocktemplate - Add missing keys: coinbaseaux, target, mutable, noncerange, sigoplimit, sizelimit, and height - Accept and send parameter Objects, checking "mode" key if present - Return rejection reason "rejected" for submit mode --- src/bitcoinrpc.cpp | 115 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 95 insertions(+), 20 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index d8b9782e3..94d46b69d 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1862,24 +1862,43 @@ Value getwork(const Array& params, bool fHelp) } -Value getmemorypool(const Array& params, bool fHelp) +Value getblocktemplate(const Array& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() != 1) throw runtime_error( - "getmemorypool [data]\n" - "If [data] is not specified, returns data needed to construct a block to work on:\n" + "getblocktemplate [params]\n" + "If [params] does not contain a \"data\" key, returns data needed to construct a block to work on:\n" " \"version\" : block version\n" " \"previousblockhash\" : hash of current highest block\n" " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n" + " \"coinbaseaux\" : data that should be included in coinbase\n" " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" - " \"coinbaseflags\" : data that should be included in coinbase so support for new features can be judged\n" - " \"time\" : timestamp appropriate for next block\n" + " \"target\" : hash target\n" " \"mintime\" : minimum timestamp appropriate for next block\n" " \"curtime\" : current timestamp\n" + " \"mutable\" : list of ways the block template may be changed\n" + " \"noncerange\" : range of valid nonces\n" + " \"sigoplimit\" : limit of sigops in blocks\n" + " \"sizelimit\" : limit of block size\n" " \"bits\" : compressed target of next block\n" - "If [data] is specified, tries to solve the block and returns true if it was successful."); + " \"height\" : height of the next block\n" + "If [params] does contain a \"data\" key, tries to solve the block and returns null if it was successful (and \"rejected\" if not)\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); - if (params.size() == 0) + const Object& oparam = params[0].get_obj(); + std::string strMode; + { + const Value& modeval = find_value(oparam, "mode"); + if (modeval.type() == str_type) + strMode = modeval.get_str(); + else + if (find_value(oparam, "data").type() == null_type) + strMode = "template"; + else + strMode = "submit"; + } + + if (strMode == "template") { if (vNodes.empty()) throw JSONRPCError(-9, "Bitcoin is not connected!"); @@ -1914,38 +1933,93 @@ Value getmemorypool(const Array& params, bool fHelp) pblock->nNonce = 0; Array transactions; - BOOST_FOREACH(CTransaction tx, pblock->vtx) { - if(tx.IsCoinBase()) + map setTxIndex; + int i = 0; + CTxDB txdb("r"); + BOOST_FOREACH (CTransaction& tx, pblock->vtx) + { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase()) continue; - CDataStream ssTx; + Object entry; + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; + entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); - transactions.push_back(HexStr(ssTx.begin(), ssTx.end())); + entry.push_back(Pair("hash", txHash.GetHex())); + + MapPrevTx mapInputs; + map mapUnused; + bool fInvalid = false; + if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + { + entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); + + Array deps; + BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) + { + if (setTxIndex.count(inp.first)) + deps.push_back(setTxIndex[inp.first]); + } + entry.push_back(Pair("depends", deps)); + + int64_t nSigOps = tx.GetLegacySigOpCount(); + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + entry.push_back(Pair("sigops", nSigOps)); + } + + transactions.push_back(entry); + } + + Object aux; + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + static Array aMutable; + if (aMutable.empty()) + { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); } Object result; result.push_back(Pair("version", pblock->nVersion)); result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbaseaux", aux)); result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); - result.push_back(Pair("coinbaseflags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); - result.push_back(Pair("time", (int64_t)pblock->nTime)); + result.push_back(Pair("target", hashTarget.GetHex())); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("curtime", (int64_t)GetAdjustedTime())); + result.push_back(Pair("mutable", aMutable)); + result.push_back(Pair("noncerange", "00000000ffffffff")); + result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); + result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("curtime", (int64_t)pblock->nTime)); result.push_back(Pair("bits", HexBits(pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); return result; } else + if (strMode == "submit") { // Parse parameters - CDataStream ssBlock(ParseHex(params[0].get_str())); + CDataStream ssBlock(ParseHex(find_value(oparam, "data").get_str()), SER_NETWORK, PROTOCOL_VERSION); CBlock pblock; ssBlock >> pblock; - return ProcessBlock(NULL, &pblock); + bool fAccepted = ProcessBlock(NULL, &pblock); + + return fAccepted ? Value::null : "rejected"; } + + throw JSONRPCError(-8, "Invalid mode"); } Value getblockhash(const Array& params, bool fHelp) @@ -2044,7 +2118,7 @@ pair pCallTable[] = make_pair("getwork", &getwork), make_pair("listaccounts", &listaccounts), make_pair("settxfee", &settxfee), - make_pair("getmemorypool", &getmemorypool), + make_pair("getblocktemplate", &getblocktemplate), make_pair("listsinceblock", &listsinceblock), make_pair("dumpprivkey", &dumpprivkey), make_pair("importprivkey", &importprivkey) @@ -2074,7 +2148,7 @@ string pAllowInSafeMode[] = "walletlock", "validateaddress", "getwork", - "getmemorypool", + "getblocktemplate", }; set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); @@ -2499,7 +2573,7 @@ void ThreadRPCServer2(void* parg) if (valMethod.type() != str_type) throw JSONRPCError(-32600, "Method must be a string"); string strMethod = valMethod.get_str(); - if (strMethod != "getwork" && strMethod != "getmemorypool") + if (strMethod != "getwork" && strMethod != "getblocktemplate") printf("ThreadRPCServer method=%s\n", strMethod.c_str()); // Parse params @@ -2681,6 +2755,7 @@ int CommandLineRPC(int argc, char *argv[]) if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblocktemplate" && n > 0) ConvertTo(params[0]); if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); if (strMethod == "sendmany" && n > 1) {