diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 2e3f8a8e5..208c830aa 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1923,24 +1923,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!"); @@ -1985,38 +2004,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; + 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()), SER_NETWORK, PROTOCOL_VERSION); + 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 getrawmempool(const Array& params, bool fHelp) @@ -2128,7 +2202,7 @@ static const CRPCCommand vRPCCommands[] = { "getwork", &getwork, true }, { "listaccounts", &listaccounts, false }, { "settxfee", &settxfee, false }, - { "getmemorypool", &getmemorypool, true }, + { "getblocktemplate", &getblocktemplate, true }, { "listsinceblock", &listsinceblock, false }, { "dumpprivkey", &dumpprivkey, false }, { "importprivkey", &importprivkey, false }, @@ -2736,7 +2810,7 @@ void JSONRequest::parse(const Value& valRequest) if (valMethod.type() != str_type) throw JSONRPCError(-32600, "Method must be a 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 @@ -3015,6 +3089,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 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) ConvertTo(params[1]); if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]);