diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index c772da012..945695064 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -245,6 +245,7 @@ static const CRPCCommand vRPCCommands[] = { "signmessage", &signmessage, false, false }, { "verifymessage", &verifymessage, false, false }, { "getwork", &getwork, true, false }, + { "getworkex", &getworkex, true, false }, { "listaccounts", &listaccounts, false, false }, { "settxfee", &settxfee, false, false }, { "getblocktemplate", &getblocktemplate, true, false }, @@ -889,7 +890,7 @@ void JSONRequest::parse(const Value& valRequest) if (valMethod.type() != str_type) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); - if (strMethod != "getwork" && strMethod != "getblocktemplate") + if (strMethod != "getwork" && strMethod != "getworkex" && strMethod != "getblocktemplate") printf("ThreadRPCServer method=%s\n", strMethod.c_str()); // Parse params diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 8134a117f..f7f06c671 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -145,6 +145,7 @@ extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHe extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getworkex(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 33dcf2238..001da4de0 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -120,6 +120,142 @@ Value getmininginfo(const Array& params, bool fHelp) } +Value getworkex(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getworkex [data, coinbase]\n" + "If [data, coinbase] is not specified, returns extended work data.\n" + ); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Litecoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Litecoin is downloading blocks..."); + + typedef map > mapNewBlock_t; + static mapNewBlock_t mapNewBlock; // FIXME: thread safety + static vector vNewBlockTemplate; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) + delete pblocktemplate; + vNewBlockTemplate.clear(); + } + + // Clear pindexPrev so future getworks make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + pblocktemplate = CreateNewBlock(reservekey); + if (!pblocktemplate) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + vNewBlockTemplate.push_back(pblocktemplate); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); + + // Pre-build hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + CTransaction coinbaseTx = pblock->vtx[0]; + std::vector merkle = pblock->GetMerkleBranch(0); + + Object result; + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << coinbaseTx; + result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end()))); + + Array merkle_arr; + + BOOST_FOREACH(uint256 merkleh, merkle) { + printf("%s\n", merkleh.ToString().c_str()); + merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh))); + } + + result.push_back(Pair("merkle", merkle_arr)); + + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + vector coinbase; + + if(params.size() == 2) + coinbase = ParseHex(params[1].get_str()); + + if (vchData.size() != 128) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + + if(coinbase.size() == 0) + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; + else + CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; + + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, *pwalletMain, reservekey); + } +} + + Value getwork(const Array& params, bool fHelp) { if (fHelp || params.size() > 1)