RPC: initial scaninput implementation.
[novacoin.git] / src / rpcmining.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "main.h"
7 #include "db.h"
8 #include "txdb.h"
9 #include "init.h"
10 #include "miner.h"
11 #include "kernel.h"
12 #include "bitcoinrpc.h"
13
14 using namespace json_spirit;
15 using namespace std;
16
17 extern uint256 nPoWBase;
18
19 Value getsubsidy(const Array& params, bool fHelp)
20 {
21     if (fHelp || params.size() > 1)
22         throw runtime_error(
23             "getsubsidy [nTarget]\n"
24             "Returns proof-of-work subsidy value for the specified value of target.");
25
26     unsigned int nBits = 0;
27
28     if (params.size() != 0)
29     {
30         CBigNum bnTarget(uint256(params[0].get_str()));
31         nBits = bnTarget.GetCompact();
32     }
33     else
34     {
35         nBits = GetNextTargetRequired(pindexBest, false);
36     }
37
38     return (uint64_t)GetProofOfWorkReward(nBits);
39 }
40
41 Value getmininginfo(const Array& params, bool fHelp)
42 {
43     if (fHelp || params.size() != 0)
44         throw runtime_error(
45             "getmininginfo\n"
46             "Returns an object containing mining-related information.");
47
48     float nKernelsRate = 0, nCoinDaysRate = 0;
49     pwalletMain->GetStakeStats(nKernelsRate, nCoinDaysRate);
50
51     Object obj, diff, weight;
52     obj.push_back(Pair("blocks",        (int)nBestHeight));
53     obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize));
54     obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx));
55
56     diff.push_back(Pair("proof-of-work",        GetDifficulty()));
57     diff.push_back(Pair("proof-of-stake",       GetDifficulty(GetLastBlockIndex(pindexBest, true))));
58     diff.push_back(Pair("search-interval",      (int)nLastCoinStakeSearchInterval));
59     obj.push_back(Pair("difficulty",    diff));
60
61     obj.push_back(Pair("blockvalue",    (uint64_t)GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits)));
62     obj.push_back(Pair("netmhashps",     GetPoWMHashPS()));
63     obj.push_back(Pair("netstakeweight", GetPoSKernelPS()));
64     obj.push_back(Pair("errors",        GetWarnings("statusbar")));
65     obj.push_back(Pair("pooledtx",      (uint64_t)mempool.size()));
66
67     weight.push_back(Pair("kernelsrate",   nKernelsRate));
68     weight.push_back(Pair("cdaysrate",   nCoinDaysRate));
69     obj.push_back(Pair("stakestats", weight));
70
71     obj.push_back(Pair("stakeinterest",    (uint64_t)GetProofOfStakeReward(0, GetLastBlockIndex(pindexBest, true)->nBits, GetLastBlockIndex(pindexBest, true)->nTime, true)));
72     obj.push_back(Pair("testnet",       fTestNet));
73     return obj;
74 }
75
76 Value scaninput(const Array& params, bool fHelp)
77 {
78     if (fHelp || params.size() > 4 || params.size() < 2)
79         throw runtime_error(
80             "scaninput <txid> <nout> [difficulty] [days]\n"
81             "Scan specified input for suitable kernel solutions.\n"
82             "    [difficulty] - upper limit for difficulty, current difficulty by default;\n"
83             "    [days] - time window, 365 days by default.\n"
84         );
85
86
87     uint256 hash;
88     hash.SetHex(params[0].get_str());
89
90     uint32_t nOut = params[1].get_int(), nBits = GetNextTargetRequired(pindexBest, true), nDays = 365;
91
92     if (params.size() > 2)
93     {
94         CBigNum bnTarget(nPoWBase);
95         bnTarget *= 1000;
96         bnTarget /= (int) (params[2].get_real() * 1000);
97         nBits = bnTarget.GetCompact();
98     }
99
100     if (params.size() > 3)
101     {
102         nDays = params[3].get_int();
103     }
104
105     CTransaction tx;
106     uint256 hashBlock = 0;
107     if (GetTransaction(hash, tx, hashBlock))
108     {
109         std::pair<uint256, uint32_t> solution;
110         if (ScanInputForStakeKernelHash(tx, nOut, nBits, nDays * 86400, solution))
111         {
112             Object r;
113             r.push_back(Pair("hash", solution.first.GetHex()));
114             r.push_back(Pair("time", DateTimeStrFormat(solution.second)));
115
116             return r;
117         }
118     }
119     else
120         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
121
122     return Value::null;
123 }
124
125 Value getworkex(const Array& params, bool fHelp)
126 {
127     if (fHelp || params.size() > 2)
128         throw runtime_error(
129             "getworkex [data, coinbase]\n"
130             "If [data, coinbase] is not specified, returns extended work data.\n"
131         );
132
133     if (vNodes.empty())
134         throw JSONRPCError(-9, "NovaCoin is not connected!");
135
136     if (IsInitialBlockDownload())
137         throw JSONRPCError(-10, "NovaCoin is downloading blocks...");
138
139     typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
140     static mapNewBlock_t mapNewBlock;
141     static vector<CBlock*> vNewBlock;
142     static CReserveKey reservekey(pwalletMain);
143
144     if (params.size() == 0)
145     {
146         // Update block
147         static unsigned int nTransactionsUpdatedLast;
148         static CBlockIndex* pindexPrev;
149         static int64_t nStart;
150         static CBlock* pblock;
151         if (pindexPrev != pindexBest ||
152             (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
153         {
154             if (pindexPrev != pindexBest)
155             {
156                 // Deallocate old blocks since they're obsolete now
157                 mapNewBlock.clear();
158                 BOOST_FOREACH(CBlock* pblock, vNewBlock)
159                     delete pblock;
160                 vNewBlock.clear();
161             }
162             nTransactionsUpdatedLast = nTransactionsUpdated;
163             pindexPrev = pindexBest;
164             nStart = GetTime();
165
166             // Create new block
167             pblock = CreateNewBlock(pwalletMain);
168             if (!pblock)
169                 throw JSONRPCError(-7, "Out of memory");
170             vNewBlock.push_back(pblock);
171         }
172
173         // Update nTime
174         pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
175         pblock->nNonce = 0;
176
177         // Update nExtraNonce
178         static unsigned int nExtraNonce = 0;
179         IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
180
181         // Save
182         mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
183
184         // Prebuild hash buffers
185         char pmidstate[32];
186         char pdata[128];
187         char phash1[64];
188         FormatHashBuffers(pblock, pmidstate, pdata, phash1);
189
190         uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
191
192         CTransaction coinbaseTx = pblock->vtx[0];
193         std::vector<uint256> merkle = pblock->GetMerkleBranch(0);
194
195         Object result;
196         result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
197         result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));
198
199         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
200         ssTx << coinbaseTx;
201         result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end())));
202
203         Array merkle_arr;
204
205         BOOST_FOREACH(uint256 merkleh, merkle) {
206             merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh)));
207         }
208
209         result.push_back(Pair("merkle", merkle_arr));
210
211
212         return result;
213     }
214     else
215     {
216         // Parse parameters
217         vector<unsigned char> vchData = ParseHex(params[0].get_str());
218         vector<unsigned char> coinbase;
219
220         if(params.size() == 2)
221             coinbase = ParseHex(params[1].get_str());
222
223         if (vchData.size() != 128)
224             throw JSONRPCError(-8, "Invalid parameter");
225
226         CBlock* pdata = (CBlock*)&vchData[0];
227
228         // Byte reverse
229         for (int i = 0; i < 128/4; i++)
230             ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
231
232         // Get saved block
233         if (!mapNewBlock.count(pdata->hashMerkleRoot))
234             return false;
235         CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
236
237         pblock->nTime = pdata->nTime;
238         pblock->nNonce = pdata->nNonce;
239
240         if(coinbase.size() == 0)
241             pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
242         else
243             CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - HACK!
244
245         pblock->hashMerkleRoot = pblock->BuildMerkleTree();
246
247         return CheckWork(pblock, *pwalletMain, reservekey);
248     }
249 }
250
251
252 Value getwork(const Array& params, bool fHelp)
253 {
254     if (fHelp || params.size() > 1)
255         throw runtime_error(
256             "getwork [data]\n"
257             "If [data] is not specified, returns formatted hash data to work on:\n"
258             "  \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
259             "  \"data\" : block data\n"
260             "  \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
261             "  \"target\" : little endian hash target\n"
262             "If [data] is specified, tries to solve the block and returns true if it was successful.");
263
264     if (vNodes.empty())
265         throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "NovaCoin is not connected!");
266
267     if (IsInitialBlockDownload())
268         throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "NovaCoin is downloading blocks...");
269
270     typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
271     static mapNewBlock_t mapNewBlock;    // FIXME: thread safety
272     static vector<CBlock*> vNewBlock;
273     static CReserveKey reservekey(pwalletMain);
274
275     if (params.size() == 0)
276     {
277         // Update block
278         static unsigned int nTransactionsUpdatedLast;
279         static CBlockIndex* pindexPrev;
280         static int64_t nStart;
281         static CBlock* pblock;
282         if (pindexPrev != pindexBest ||
283             (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
284         {
285             if (pindexPrev != pindexBest)
286             {
287                 // Deallocate old blocks since they're obsolete now
288                 mapNewBlock.clear();
289                 BOOST_FOREACH(CBlock* pblock, vNewBlock)
290                     delete pblock;
291                 vNewBlock.clear();
292             }
293
294             // Clear pindexPrev so future getworks make a new block, despite any failures from here on
295             pindexPrev = NULL;
296
297             // Store the pindexBest used before CreateNewBlock, to avoid races
298             nTransactionsUpdatedLast = nTransactionsUpdated;
299             CBlockIndex* pindexPrevNew = pindexBest;
300             nStart = GetTime();
301
302             // Create new block
303             pblock = CreateNewBlock(pwalletMain);
304             if (!pblock)
305                 throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
306             vNewBlock.push_back(pblock);
307
308             // Need to update only after we know CreateNewBlock succeeded
309             pindexPrev = pindexPrevNew;
310         }
311
312         // Update nTime
313         pblock->UpdateTime(pindexPrev);
314         pblock->nNonce = 0;
315
316         // Update nExtraNonce
317         static unsigned int nExtraNonce = 0;
318         IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
319
320         // Save
321         mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
322
323         // Pre-build hash buffers
324         char pmidstate[32];
325         char pdata[128];
326         char phash1[64];
327         FormatHashBuffers(pblock, pmidstate, pdata, phash1);
328
329         uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
330
331         Object result;
332         result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
333         result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
334         result.push_back(Pair("hash1",    HexStr(BEGIN(phash1), END(phash1)))); // deprecated
335         result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));
336         return result;
337     }
338     else
339     {
340         // Parse parameters
341         vector<unsigned char> vchData = ParseHex(params[0].get_str());
342         if (vchData.size() != 128)
343             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
344         CBlock* pdata = (CBlock*)&vchData[0];
345
346         // Byte reverse
347         for (int i = 0; i < 128/4; i++)
348             ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
349
350         // Get saved block
351         if (!mapNewBlock.count(pdata->hashMerkleRoot))
352             return false;
353         CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
354
355         pblock->nTime = pdata->nTime;
356         pblock->nNonce = pdata->nNonce;
357         pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
358         pblock->hashMerkleRoot = pblock->BuildMerkleTree();
359
360         return CheckWork(pblock, *pwalletMain, reservekey);
361     }
362 }
363
364
365 Value getblocktemplate(const Array& params, bool fHelp)
366 {
367     if (fHelp || params.size() > 1)
368         throw runtime_error(
369             "getblocktemplate [params]\n"
370             "Returns data needed to construct a block to work on:\n"
371             "  \"version\" : block version\n"
372             "  \"previousblockhash\" : hash of current highest block\n"
373             "  \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
374             "  \"coinbaseaux\" : data that should be included in coinbase\n"
375             "  \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
376             "  \"target\" : hash target\n"
377             "  \"mintime\" : minimum timestamp appropriate for next block\n"
378             "  \"curtime\" : current timestamp\n"
379             "  \"mutable\" : list of ways the block template may be changed\n"
380             "  \"noncerange\" : range of valid nonces\n"
381             "  \"sigoplimit\" : limit of sigops in blocks\n"
382             "  \"sizelimit\" : limit of block size\n"
383             "  \"bits\" : compressed target of next block\n"
384             "  \"height\" : height of the next block\n"
385             "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");
386
387     std::string strMode = "template";
388     if (params.size() > 0)
389     {
390         const Object& oparam = params[0].get_obj();
391         const Value& modeval = find_value(oparam, "mode");
392         if (modeval.type() == str_type)
393             strMode = modeval.get_str();
394         else if (modeval.type() == null_type)
395         {
396             /* Do nothing */
397         }
398         else
399             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
400     }
401
402     if (strMode != "template")
403         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
404
405     if (vNodes.empty())
406         throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "NovaCoin is not connected!");
407
408     if (IsInitialBlockDownload())
409         throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "NovaCoin is downloading blocks...");
410
411     static CReserveKey reservekey(pwalletMain);
412
413     // Update block
414     static unsigned int nTransactionsUpdatedLast;
415     static CBlockIndex* pindexPrev;
416     static int64_t nStart;
417     static CBlock* pblock;
418     if (pindexPrev != pindexBest ||
419         (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
420     {
421         // Clear pindexPrev so future calls make a new block, despite any failures from here on
422         pindexPrev = NULL;
423
424         // Store the pindexBest used before CreateNewBlock, to avoid races
425         nTransactionsUpdatedLast = nTransactionsUpdated;
426         CBlockIndex* pindexPrevNew = pindexBest;
427         nStart = GetTime();
428
429         // Create new block
430         if(pblock)
431         {
432             delete pblock;
433             pblock = NULL;
434         }
435         pblock = CreateNewBlock(pwalletMain);
436         if (!pblock)
437             throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
438
439         // Need to update only after we know CreateNewBlock succeeded
440         pindexPrev = pindexPrevNew;
441     }
442
443     // Update nTime
444     pblock->UpdateTime(pindexPrev);
445     pblock->nNonce = 0;
446
447     Array transactions;
448     map<uint256, int64_t> setTxIndex;
449     int i = 0;
450     CTxDB txdb("r");
451     BOOST_FOREACH (CTransaction& tx, pblock->vtx)
452     {
453         uint256 txHash = tx.GetHash();
454         setTxIndex[txHash] = i++;
455
456         if (tx.IsCoinBase() || tx.IsCoinStake())
457             continue;
458
459         Object entry;
460
461         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
462         ssTx << tx;
463         entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));
464
465         entry.push_back(Pair("hash", txHash.GetHex()));
466
467         MapPrevTx mapInputs;
468         map<uint256, CTxIndex> mapUnused;
469         bool fInvalid = false;
470         if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
471         {
472             entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut())));
473
474             Array deps;
475             BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs)
476             {
477                 if (setTxIndex.count(inp.first))
478                     deps.push_back(setTxIndex[inp.first]);
479             }
480             entry.push_back(Pair("depends", deps));
481
482             int64_t nSigOps = tx.GetLegacySigOpCount();
483             nSigOps += tx.GetP2SHSigOpCount(mapInputs);
484             entry.push_back(Pair("sigops", nSigOps));
485         }
486
487         transactions.push_back(entry);
488     }
489
490     Object aux;
491     aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
492
493     uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
494
495     static Array aMutable;
496     if (aMutable.empty())
497     {
498         aMutable.push_back("time");
499         aMutable.push_back("transactions");
500         aMutable.push_back("prevblock");
501     }
502
503     Object result;
504     result.push_back(Pair("version", pblock->nVersion));
505     result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
506     result.push_back(Pair("transactions", transactions));
507     result.push_back(Pair("coinbaseaux", aux));
508     result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
509     result.push_back(Pair("target", hashTarget.GetHex()));
510     result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
511     result.push_back(Pair("mutable", aMutable));
512     result.push_back(Pair("noncerange", "00000000ffffffff"));
513     result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
514     result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
515     result.push_back(Pair("curtime", (int64_t)pblock->nTime));
516     result.push_back(Pair("bits", HexBits(pblock->nBits)));
517     result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
518
519     return result;
520 }
521
522 Value submitblock(const Array& params, bool fHelp)
523 {
524     if (fHelp || params.size() < 1 || params.size() > 2)
525         throw runtime_error(
526             "submitblock <hex data> [optional-params-obj]\n"
527             "[optional-params-obj] parameter is currently ignored.\n"
528             "Attempts to submit new block to network.\n"
529             "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");
530
531     vector<unsigned char> blockData(ParseHex(params[0].get_str()));
532     CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
533     CBlock block;
534     try {
535         ssBlock >> block;
536     }
537     catch (std::exception &e) {
538         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
539     }
540
541     bool fAccepted = ProcessBlock(NULL, &block);
542     if (!fAccepted)
543         return "rejected";
544
545     return Value::null;
546 }
547