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