Wallet core: remove SendMoneyToDestination
[novacoin.git] / src / rpcwallet.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 "wallet.h"
7 #include "walletdb.h"
8 #include "bitcoinrpc.h"
9 #include "init.h"
10 #include "util.h"
11 #include "ntp.h"
12 #include "base58.h"
13
14 using namespace json_spirit;
15 using namespace std;
16
17 int64_t nWalletUnlockTime;
18 static CCriticalSection cs_nWalletUnlockTime;
19
20 extern int64_t nReserveBalance;
21 extern void TxToJSON(const CTransaction& tx, const uint256& hashBlock, json_spirit::Object& entry);
22
23 std::string HelpRequiringPassphrase()
24 {
25     return pwalletMain->IsCrypted()
26         ? "\n\nRequires wallet passphrase to be set with walletpassphrase first"
27         : "";
28 }
29
30 void EnsureWalletIsUnlocked()
31 {
32     if (pwalletMain->IsLocked())
33         throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
34     if (fWalletUnlockMintOnly)
35         throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
36 }
37
38 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
39 {
40     int confirms = wtx.GetDepthInMainChain();
41     entry.push_back(Pair("confirmations", confirms));
42     if (wtx.IsCoinBase() || wtx.IsCoinStake())
43         entry.push_back(Pair("generated", true));
44     if (confirms)
45     {
46         entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
47         entry.push_back(Pair("blockindex", wtx.nIndex));
48         entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
49     }
50     entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
51     entry.push_back(Pair("time", (int64_t)wtx.GetTxTime()));
52     entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
53     BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
54         entry.push_back(Pair(item.first, item.second));
55 }
56
57 string AccountFromValue(const Value& value)
58 {
59     string strAccount = value.get_str();
60     if (strAccount == "*")
61         throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
62     return strAccount;
63 }
64
65 Value getinfo(const Array& params, bool fHelp)
66 {
67     if (fHelp || params.size() != 0)
68         throw runtime_error(
69             "getinfo\n"
70             "Returns an object containing various state info.");
71
72     proxyType proxy;
73     GetProxy(NET_IPV4, proxy);
74
75     Object obj, diff, timestamping;
76     obj.push_back(Pair("version",       FormatFullVersion()));
77     obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
78     obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
79     obj.push_back(Pair("balance",       ValueFromAmount(pwalletMain->GetBalance())));
80     obj.push_back(Pair("unspendable",       ValueFromAmount(pwalletMain->GetWatchOnlyBalance())));
81     obj.push_back(Pair("newmint",       ValueFromAmount(pwalletMain->GetNewMint())));
82     obj.push_back(Pair("stake",         ValueFromAmount(pwalletMain->GetStake())));
83     obj.push_back(Pair("blocks",        (int)nBestHeight));
84
85     timestamping.push_back(Pair("systemclock", GetTime()));
86     timestamping.push_back(Pair("adjustedtime", GetAdjustedTime()));
87
88     int64_t nNtpOffset = GetNtpOffset(),
89             nP2POffset = GetNodesOffset();
90
91     timestamping.push_back(Pair("ntpoffset", nNtpOffset != INT64_MAX ? nNtpOffset : Value::null));
92     timestamping.push_back(Pair("p2poffset", nP2POffset != INT64_MAX ? nP2POffset : Value::null));
93
94     obj.push_back(Pair("timestamping", timestamping));
95
96     obj.push_back(Pair("moneysupply",   ValueFromAmount(pindexBest->nMoneySupply)));
97     obj.push_back(Pair("connections",   (int)vNodes.size()));
98     obj.push_back(Pair("proxy",         (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
99     obj.push_back(Pair("ip",            addrSeenByPeer.ToStringIP()));
100
101     diff.push_back(Pair("proof-of-work",  GetDifficulty()));
102     diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
103     obj.push_back(Pair("difficulty",    diff));
104
105     obj.push_back(Pair("testnet",       fTestNet));
106     obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime()));
107     obj.push_back(Pair("keypoolsize",   (int)pwalletMain->GetKeyPoolSize()));
108     obj.push_back(Pair("paytxfee",      ValueFromAmount(nTransactionFee)));
109     obj.push_back(Pair("mininput",      ValueFromAmount(nMinimumInputValue)));
110     if (pwalletMain->IsCrypted())
111         obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000));
112     obj.push_back(Pair("errors",        GetWarnings("statusbar")));
113     return obj;
114 }
115
116 Value getnewaddress(const Array& params, bool fHelp)
117 {
118     if (fHelp || params.size() > 1)
119         throw runtime_error(
120             "getnewaddress [account]\n"
121             "Returns a new NovaCoin address for receiving payments.  "
122             "If [account] is specified (recommended), it is added to the address book "
123             "so payments received with the address will be credited to [account].");
124
125     // Parse the account first so we don't generate a key if there's an error
126     string strAccount;
127     if (params.size() > 0)
128         strAccount = AccountFromValue(params[0]);
129
130     if (!pwalletMain->IsLocked())
131         pwalletMain->TopUpKeyPool();
132
133     // Generate a new key that is added to wallet
134     CPubKey newKey;
135     if (!pwalletMain->GetKeyFromPool(newKey, false))
136         throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
137     CKeyID keyID = newKey.GetID();
138
139     pwalletMain->SetAddressBookName(keyID, strAccount);
140
141     return CBitcoinAddress(keyID).ToString();
142 }
143
144
145 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
146 {
147     CWalletDB walletdb(pwalletMain->strWalletFile);
148
149     CAccount account;
150     walletdb.ReadAccount(strAccount, account);
151
152     bool bKeyUsed = false;
153
154     // Check if the current key has been used
155     if (account.vchPubKey.IsValid())
156     {
157         CScript scriptPubKey;
158         scriptPubKey.SetDestination(account.vchPubKey.GetID());
159         for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
160              it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
161              ++it)
162         {
163             const CWalletTx& wtx = (*it).second;
164             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
165                 if (txout.scriptPubKey == scriptPubKey)
166                     bKeyUsed = true;
167         }
168     }
169
170     // Generate a new key
171     if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
172     {
173         if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
174             throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
175
176         pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
177         walletdb.WriteAccount(strAccount, account);
178     }
179
180     return CBitcoinAddress(account.vchPubKey.GetID());
181 }
182
183 Value getaccountaddress(const Array& params, bool fHelp)
184 {
185     if (fHelp || params.size() != 1)
186         throw runtime_error(
187             "getaccountaddress <account>\n"
188             "Returns the current NovaCoin address for receiving payments to this account.");
189
190     // Parse the account first so we don't generate a key if there's an error
191     string strAccount = AccountFromValue(params[0]);
192
193     Value ret;
194
195     ret = GetAccountAddress(strAccount).ToString();
196
197     return ret;
198 }
199
200
201
202 Value setaccount(const Array& params, bool fHelp)
203 {
204     if (fHelp || params.size() < 1 || params.size() > 2)
205         throw runtime_error(
206             "setaccount <novacoinaddress> <account>\n"
207             "Sets the account associated with the given address.");
208
209     CBitcoinAddress address(params[0].get_str());
210     if (!address.IsValid())
211         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
212
213
214     string strAccount;
215     if (params.size() > 1)
216         strAccount = AccountFromValue(params[1]);
217
218     // Detect when changing the account of an address that is the 'unused current key' of another account:
219     if (pwalletMain->mapAddressBook.count(address.Get()))
220     {
221         string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
222         if (address == GetAccountAddress(strOldAccount))
223             GetAccountAddress(strOldAccount, true);
224     }
225
226     pwalletMain->SetAddressBookName(address.Get(), strAccount);
227
228     return Value::null;
229 }
230
231
232 Value getaccount(const Array& params, bool fHelp)
233 {
234     if (fHelp || params.size() != 1)
235         throw runtime_error(
236             "getaccount <novacoinaddress>\n"
237             "Returns the account associated with the given address.");
238
239     CBitcoinAddress address(params[0].get_str());
240     if (!address.IsValid())
241         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
242
243     string strAccount;
244     map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
245     if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
246         strAccount = (*mi).second;
247     return strAccount;
248 }
249
250
251 Value getaddressesbyaccount(const Array& params, bool fHelp)
252 {
253     if (fHelp || params.size() != 1)
254         throw runtime_error(
255             "getaddressesbyaccount <account>\n"
256             "Returns the list of addresses for the given account.");
257
258     string strAccount = AccountFromValue(params[0]);
259
260     // Find all addresses that have the given account
261     Array ret;
262     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
263     {
264         const CBitcoinAddress& address = item.first;
265         const string& strName = item.second;
266         if (strName == strAccount)
267             ret.push_back(address.ToString());
268     }
269     return ret;
270 }
271
272 Value mergecoins(const Array& params, bool fHelp)
273 {
274     if (fHelp || params.size() != 3)
275         throw runtime_error(
276             "mergecoins <amount> <minvalue> <outputvalue>\n"
277             "<amount> is resulting inputs sum\n"
278             "<minvalue> is minimum value of inputs which are used in join process\n"
279             "<outputvalue> is resulting value of inputs which will be created\n"
280             "All values are real and and rounded to the nearest " + FormatMoney(nMinimumInputValue)
281             + HelpRequiringPassphrase());
282
283     if (pwalletMain->IsLocked())
284         throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
285
286     // Total amount
287     int64_t nAmount = AmountFromValue(params[0]);
288
289     // Min input amount
290     int64_t nMinValue = AmountFromValue(params[1]);
291
292     // Output amount
293     int64_t nOutputValue = AmountFromValue(params[2]);
294
295     if (nAmount < nMinimumInputValue)
296         throw JSONRPCError(-101, "Send amount too small");
297
298     if (nMinValue < nMinimumInputValue)
299         throw JSONRPCError(-101, "Max value too small");
300
301     if (nOutputValue < nMinimumInputValue)
302         throw JSONRPCError(-101, "Output value too small");
303
304     if (nOutputValue < nMinValue)
305         throw JSONRPCError(-101, "Output value is lower than min value");
306
307     list<uint256> listMerged;
308     if (!pwalletMain->MergeCoins(nAmount, nMinValue, nOutputValue, listMerged))
309         return Value::null;
310
311     Array mergedHashes;
312     BOOST_FOREACH(const uint256 txHash, listMerged)
313         mergedHashes.push_back(txHash.GetHex());
314
315     return mergedHashes;
316 }
317
318 Value sendtoaddress(const Array& params, bool fHelp)
319 {
320     if (fHelp || params.size() < 2 || params.size() > 4)
321         throw runtime_error(
322             "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
323             "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
324             + HelpRequiringPassphrase());
325
326     CBitcoinAddress address(params[0].get_str());
327     if (!address.IsValid())
328         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
329
330     // Amount
331     int64_t nAmount = AmountFromValue(params[1]);
332
333     if (nAmount < nMinimumInputValue)
334         throw JSONRPCError(-101, "Send amount too small");
335
336     // Wallet comments
337     CWalletTx wtx;
338     if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
339         wtx.mapValue["comment"] = params[2].get_str();
340     if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
341         wtx.mapValue["to"]      = params[3].get_str();
342
343     if (pwalletMain->IsLocked())
344         throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
345
346     // Parse Bitcoin address
347     CScript scriptPubKey;
348     scriptPubKey.SetDestination(address.Get());
349
350     string strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
351     if (strError != "")
352         throw JSONRPCError(RPC_WALLET_ERROR, strError);
353
354     return wtx.GetHash().GetHex();
355 }
356
357 Value listaddressgroupings(const Array& params, bool fHelp)
358 {
359     if (fHelp)
360         throw runtime_error(
361             "listaddressgroupings\n"
362             "Lists groups of addresses which have had their common ownership\n"
363             "made public by common use as inputs or as the resulting change\n"
364             "in past transactions");
365
366     Array jsonGroupings;
367     map<CTxDestination, int64_t> balances = pwalletMain->GetAddressBalances();
368     BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
369     {
370         Array jsonGrouping;
371         BOOST_FOREACH(CTxDestination address, grouping)
372         {
373             Array addressInfo;
374             addressInfo.push_back(CBitcoinAddress(address).ToString());
375             addressInfo.push_back(ValueFromAmount(balances[address]));
376             {
377                 LOCK(pwalletMain->cs_wallet);
378                 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
379                     addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
380             }
381             jsonGrouping.push_back(addressInfo);
382         }
383         jsonGroupings.push_back(jsonGrouping);
384     }
385     return jsonGroupings;
386 }
387
388 Value signmessage(const Array& params, bool fHelp)
389 {
390     if (fHelp || params.size() != 2)
391         throw runtime_error(
392             "signmessage <novacoinaddress> <message>\n"
393             "Sign a message with the private key of an address");
394
395     EnsureWalletIsUnlocked();
396
397     string strAddress = params[0].get_str();
398     string strMessage = params[1].get_str();
399
400     CBitcoinAddress addr(strAddress);
401     if (!addr.IsValid())
402         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
403
404     CKeyID keyID;
405     if (!addr.GetKeyID(keyID))
406         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
407
408     CKey key;
409     if (!pwalletMain->GetKey(keyID, key))
410         throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
411
412     CDataStream ss(SER_GETHASH, 0);
413     ss << strMessageMagic;
414     ss << strMessage;
415
416     vector<unsigned char> vchSig;
417     if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
418         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
419
420     return EncodeBase64(&vchSig[0], vchSig.size());
421 }
422
423 Value verifymessage(const Array& params, bool fHelp)
424 {
425     if (fHelp || params.size() != 3)
426         throw runtime_error(
427             "verifymessage <novacoinaddress> <signature> <message>\n"
428             "Verify a signed message");
429
430     string strAddress  = params[0].get_str();
431     string strSign     = params[1].get_str();
432     string strMessage  = params[2].get_str();
433
434     CBitcoinAddress addr(strAddress);
435     if (!addr.IsValid())
436         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
437
438     CKeyID keyID;
439     if (!addr.GetKeyID(keyID))
440         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
441
442     bool fInvalid = false;
443     vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
444
445     if (fInvalid)
446         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
447
448     CDataStream ss(SER_GETHASH, 0);
449     ss << strMessageMagic;
450     ss << strMessage;
451
452     CKey key;
453     if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
454         return false;
455
456     return (key.GetPubKey().GetID() == keyID);
457 }
458
459
460 Value getreceivedbyaddress(const Array& params, bool fHelp)
461 {
462     if (fHelp || params.size() < 1 || params.size() > 2)
463         throw runtime_error(
464             "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
465             "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
466
467     // Bitcoin address
468     CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
469     CScript scriptPubKey;
470     if (!address.IsValid())
471         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
472     scriptPubKey.SetDestination(address.Get());
473     if (!IsMine(*pwalletMain,scriptPubKey))
474         return 0.0;
475
476     // Minimum confirmations
477     int nMinDepth = 1;
478     if (params.size() > 1)
479         nMinDepth = params[1].get_int();
480
481     // Tally
482     int64_t nAmount = 0;
483     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
484     {
485         const CWalletTx& wtx = (*it).second;
486         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
487             continue;
488
489         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
490             if (txout.scriptPubKey == scriptPubKey)
491                 if (wtx.GetDepthInMainChain() >= nMinDepth)
492                     nAmount += txout.nValue;
493     }
494
495     return  ValueFromAmount(nAmount);
496 }
497
498
499 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
500 {
501     BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
502     {
503         const CTxDestination& address = item.first;
504         const string& strName = item.second;
505         if (strName == strAccount)
506             setAddress.insert(address);
507     }
508 }
509
510 Value getreceivedbyaccount(const Array& params, bool fHelp)
511 {
512     if (fHelp || params.size() < 1 || params.size() > 2)
513         throw runtime_error(
514             "getreceivedbyaccount <account> [minconf=1]\n"
515             "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
516
517     // Minimum confirmations
518     int nMinDepth = 1;
519     if (params.size() > 1)
520         nMinDepth = params[1].get_int();
521
522     // Get the set of pub keys assigned to account
523     string strAccount = AccountFromValue(params[0]);
524     set<CTxDestination> setAddress;
525     GetAccountAddresses(strAccount, setAddress);
526
527     // Tally
528     int64_t nAmount = 0;
529     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
530     {
531         const CWalletTx& wtx = (*it).second;
532         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
533             continue;
534
535         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
536         {
537             CTxDestination address;
538             if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
539                 if (wtx.GetDepthInMainChain() >= nMinDepth)
540                     nAmount += txout.nValue;
541         }
542     }
543
544     return (double)nAmount / (double)COIN;
545 }
546
547
548 int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
549 {
550     int64_t nBalance = 0;
551
552     // Tally wallet transactions
553     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
554     {
555         const CWalletTx& wtx = (*it).second;
556         if (!wtx.IsFinal())
557             continue;
558
559         int64_t nGenerated, nReceived, nSent, nFee;
560         wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter);
561
562         if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
563             nBalance += nReceived;
564         nBalance += nGenerated - nSent - nFee;
565     }
566
567     // Tally internal accounting entries
568     nBalance += walletdb.GetAccountCreditDebit(strAccount);
569
570     return nBalance;
571 }
572
573 int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
574 {
575     CWalletDB walletdb(pwalletMain->strWalletFile);
576     return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
577 }
578
579
580 Value getbalance(const Array& params, bool fHelp)
581 {
582     if (fHelp || params.size() > 2)
583         throw runtime_error(
584             "getbalance [account] [minconf=1] [watchonly=0]\n"
585             "If [account] is not specified, returns the server's total available balance.\n"
586             "If [account] is specified, returns the balance in the account.\n"
587             "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
588
589     if (params.size() == 0)
590         return  ValueFromAmount(pwalletMain->GetBalance());
591
592     int nMinDepth = 1;
593     if (params.size() > 1)
594         nMinDepth = params[1].get_int();
595     isminefilter filter = MINE_SPENDABLE;
596     if(params.size() > 2)
597         if(params[2].get_bool())
598             filter = filter | MINE_WATCH_ONLY;
599
600     if (params[0].get_str() == "*") {
601         // Calculate total balance a different way from GetBalance()
602         // (GetBalance() sums up all unspent TxOuts)
603         // getbalance and getbalance '*' 0 should return the same number.
604         int64_t nBalance = 0;
605         for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
606         {
607             const CWalletTx& wtx = (*it).second;
608             if (!wtx.IsTrusted())
609                 continue;
610
611             int64_t allGeneratedImmature, allGeneratedMature, allFee;
612             allGeneratedImmature = allGeneratedMature = allFee = 0;
613
614             string strSentAccount;
615             list<pair<CTxDestination, int64_t> > listReceived;
616             list<pair<CTxDestination, int64_t> > listSent;
617             wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
618             if (wtx.GetDepthInMainChain() >= nMinDepth)
619             {
620                 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
621                     nBalance += r.second;
622             }
623             BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent)
624                 nBalance -= r.second;
625             nBalance -= allFee;
626             nBalance += allGeneratedMature;
627         }
628         return  ValueFromAmount(nBalance);
629     }
630
631     string strAccount = AccountFromValue(params[0]);
632
633     int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
634
635     return ValueFromAmount(nBalance);
636 }
637
638
639 Value movecmd(const Array& params, bool fHelp)
640 {
641     if (fHelp || params.size() < 3 || params.size() > 5)
642         throw runtime_error(
643             "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
644             "Move from one account in your wallet to another.");
645
646     string strFrom = AccountFromValue(params[0]);
647     string strTo = AccountFromValue(params[1]);
648     int64_t nAmount = AmountFromValue(params[2]);
649
650     if (nAmount < nMinimumInputValue)
651         throw JSONRPCError(-101, "Send amount too small");
652
653     if (params.size() > 3)
654         // unused parameter, used to be nMinDepth, keep type-checking it though
655         (void)params[3].get_int();
656     string strComment;
657     if (params.size() > 4)
658         strComment = params[4].get_str();
659
660     CWalletDB walletdb(pwalletMain->strWalletFile);
661     if (!walletdb.TxnBegin())
662         throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
663
664     int64_t nNow = GetAdjustedTime();
665
666     // Debit
667     CAccountingEntry debit;
668     debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
669     debit.strAccount = strFrom;
670     debit.nCreditDebit = -nAmount;
671     debit.nTime = nNow;
672     debit.strOtherAccount = strTo;
673     debit.strComment = strComment;
674     walletdb.WriteAccountingEntry(debit);
675
676     // Credit
677     CAccountingEntry credit;
678     credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
679     credit.strAccount = strTo;
680     credit.nCreditDebit = nAmount;
681     credit.nTime = nNow;
682     credit.strOtherAccount = strFrom;
683     credit.strComment = strComment;
684     walletdb.WriteAccountingEntry(credit);
685
686     if (!walletdb.TxnCommit())
687         throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
688
689     return true;
690 }
691
692
693 Value sendfrom(const Array& params, bool fHelp)
694 {
695     if (fHelp || params.size() < 3 || params.size() > 6)
696         throw runtime_error(
697             "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
698             "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
699             + HelpRequiringPassphrase());
700
701     string strAccount = AccountFromValue(params[0]);
702     CBitcoinAddress address(params[1].get_str());
703     if (!address.IsValid())
704         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
705     int64_t nAmount = AmountFromValue(params[2]);
706
707     if (nAmount < nMinimumInputValue)
708         throw JSONRPCError(-101, "Send amount too small");
709
710     int nMinDepth = 1;
711     if (params.size() > 3)
712         nMinDepth = params[3].get_int();
713
714     CWalletTx wtx;
715     wtx.strFromAccount = strAccount;
716     if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
717         wtx.mapValue["comment"] = params[4].get_str();
718     if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
719         wtx.mapValue["to"]      = params[5].get_str();
720
721     EnsureWalletIsUnlocked();
722
723     // Check funds
724     int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
725     if (nAmount > nBalance)
726         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
727
728     // Parse Bitcoin address
729     CScript scriptPubKey;
730     scriptPubKey.SetDestination(address.Get());
731
732     // Send
733     string strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
734     if (strError != "")
735         throw JSONRPCError(RPC_WALLET_ERROR, strError);
736
737     return wtx.GetHash().GetHex();
738 }
739
740
741 Value sendmany(const Array& params, bool fHelp)
742 {
743     if (fHelp || params.size() < 2 || params.size() > 4)
744         throw runtime_error(
745             "sendmany <fromaccount> '{address:amount,...}' [minconf=1] [comment]\n"
746             "amounts are double-precision floating point numbers"
747             + HelpRequiringPassphrase());
748
749     string strAccount = AccountFromValue(params[0]);
750     Object sendTo = params[1].get_obj();
751     int nMinDepth = 1;
752     if (params.size() > 2)
753         nMinDepth = params[2].get_int();
754
755     CWalletTx wtx;
756     wtx.strFromAccount = strAccount;
757     if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
758         wtx.mapValue["comment"] = params[3].get_str();
759
760     set<CBitcoinAddress> setAddress;
761     vector<pair<CScript, int64_t> > vecSend;
762
763     int64_t totalAmount = 0;
764     BOOST_FOREACH(const Pair& s, sendTo)
765     {
766         CBitcoinAddress address(s.name_);
767         if (!address.IsValid())
768             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
769
770         if (setAddress.count(address))
771             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
772         setAddress.insert(address);
773
774         CScript scriptPubKey;
775         scriptPubKey.SetDestination(address.Get());
776         int64_t nAmount = AmountFromValue(s.value_);
777
778         if (nAmount < nMinimumInputValue)
779             throw JSONRPCError(-101, "Send amount too small");
780
781         totalAmount += nAmount;
782
783         vecSend.push_back(make_pair(scriptPubKey, nAmount));
784     }
785
786     EnsureWalletIsUnlocked();
787
788     // Check funds
789     int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
790     if (totalAmount > nBalance)
791         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
792
793     // Send
794     CReserveKey keyChange(pwalletMain);
795     int64_t nFeeRequired = 0;
796     bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
797     if (!fCreated)
798     {
799         int64_t nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
800         if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
801             throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
802         throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
803     }
804     if (!pwalletMain->CommitTransaction(wtx, keyChange))
805         throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
806
807     return wtx.GetHash().GetHex();
808 }
809
810 Value addmultisigaddress(const Array& params, bool fHelp)
811 {
812     if (fHelp || params.size() < 2 || params.size() > 3)
813     {
814         string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
815             "Add a nrequired-to-sign multisignature address to the wallet\"\n"
816             "each key is a NovaCoin address or hex-encoded public key\n"
817             "If [account] is specified, assign address to [account].";
818         throw runtime_error(msg);
819     }
820
821     int nRequired = params[0].get_int();
822     const Array& keys = params[1].get_array();
823     string strAccount;
824     if (params.size() > 2)
825         strAccount = AccountFromValue(params[2]);
826
827     // Gather public keys
828     if (nRequired < 1)
829         throw runtime_error("a multisignature address must require at least one key to redeem");
830     if ((int)keys.size() < nRequired)
831         throw runtime_error(
832             strprintf("not enough keys supplied "
833                       "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
834     if (keys.size() > 16)
835         throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
836     std::vector<CKey> pubkeys;
837     pubkeys.resize(keys.size());
838     for (unsigned int i = 0; i < keys.size(); i++)
839     {
840         const std::string& ks = keys[i].get_str();
841
842         // Case 1: Bitcoin address and we have full public key:
843         CBitcoinAddress address(ks);
844         if (address.IsValid())
845         {
846             CKeyID keyID;
847             if (!address.GetKeyID(keyID))
848                 throw runtime_error(
849                     strprintf("%s does not refer to a key",ks.c_str()));
850             CPubKey vchPubKey;
851             if (!pwalletMain->GetPubKey(keyID, vchPubKey))
852                 throw runtime_error(
853                     strprintf("no full public key for address %s",ks.c_str()));
854             if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
855                 throw runtime_error(" Invalid public key: "+ks);
856         }
857
858         // Case 2: hex public key
859         else if (IsHex(ks))
860         {
861             CPubKey vchPubKey(ParseHex(ks));
862             if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
863                 throw runtime_error(" Invalid public key: "+ks);
864         }
865         else
866         {
867             throw runtime_error(" Invalid public key: "+ks);
868         }
869     }
870
871     // Construct using pay-to-script-hash:
872     CScript inner;
873     inner.SetMultisig(nRequired, pubkeys);
874
875     if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
876     throw runtime_error(
877         strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
878
879     CScriptID innerID = inner.GetID();
880     pwalletMain->AddCScript(inner);
881
882     pwalletMain->SetAddressBookName(innerID, strAccount);
883     return CBitcoinAddress(innerID).ToString();
884 }
885
886 Value addredeemscript(const Array& params, bool fHelp)
887 {
888     if (fHelp || params.size() < 1 || params.size() > 2)
889     {
890         string msg = "addredeemscript <redeemScript> [account]\n"
891             "Add a P2SH address with a specified redeemScript to the wallet.\n"
892             "If [account] is specified, assign address to [account].";
893         throw runtime_error(msg);
894     }
895
896     string strAccount;
897     if (params.size() > 1)
898         strAccount = AccountFromValue(params[1]);
899
900     // Construct using pay-to-script-hash:
901     vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
902     CScript inner(innerData.begin(), innerData.end());
903     CScriptID innerID = inner.GetID();
904     pwalletMain->AddCScript(inner);
905
906     pwalletMain->SetAddressBookName(innerID, strAccount);
907     return CBitcoinAddress(innerID).ToString();
908 }
909
910 struct tallyitem
911 {
912     int64_t nAmount;
913     int nConf;
914     tallyitem()
915     {
916         nAmount = 0;
917         nConf = std::numeric_limits<int>::max();
918     }
919 };
920
921 Value ListReceived(const Array& params, bool fByAccounts)
922 {
923     // Minimum confirmations
924     int nMinDepth = 1;
925     if (params.size() > 0)
926         nMinDepth = params[0].get_int();
927
928     // Whether to include empty accounts
929     bool fIncludeEmpty = false;
930     if (params.size() > 1)
931         fIncludeEmpty = params[1].get_bool();
932
933     // Tally
934     map<CBitcoinAddress, tallyitem> mapTally;
935     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
936     {
937         const CWalletTx& wtx = (*it).second;
938
939         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
940             continue;
941
942         int nDepth = wtx.GetDepthInMainChain();
943         if (nDepth < nMinDepth)
944             continue;
945
946         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
947         {
948             CTxDestination address;
949             if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
950                 continue;
951
952             tallyitem& item = mapTally[address];
953             item.nAmount += txout.nValue;
954             item.nConf = min(item.nConf, nDepth);
955         }
956     }
957
958     // Reply
959     Array ret;
960     map<string, tallyitem> mapAccountTally;
961     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
962     {
963         const CBitcoinAddress& address = item.first;
964         const string& strAccount = item.second;
965         map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
966         if (it == mapTally.end() && !fIncludeEmpty)
967             continue;
968
969         int64_t nAmount = 0;
970         int nConf = std::numeric_limits<int>::max();
971         if (it != mapTally.end())
972         {
973             nAmount = (*it).second.nAmount;
974             nConf = (*it).second.nConf;
975         }
976
977         if (fByAccounts)
978         {
979             tallyitem& item = mapAccountTally[strAccount];
980             item.nAmount += nAmount;
981             item.nConf = min(item.nConf, nConf);
982         }
983         else
984         {
985             Object obj;
986             obj.push_back(Pair("address",       address.ToString()));
987             obj.push_back(Pair("account",       strAccount));
988             obj.push_back(Pair("amount",        ValueFromAmount(nAmount)));
989             obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
990             ret.push_back(obj);
991         }
992     }
993
994     if (fByAccounts)
995     {
996         for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
997         {
998             int64_t nAmount = (*it).second.nAmount;
999             int nConf = (*it).second.nConf;
1000             Object obj;
1001             obj.push_back(Pair("account",       (*it).first));
1002             obj.push_back(Pair("amount",        ValueFromAmount(nAmount)));
1003             obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
1004             ret.push_back(obj);
1005         }
1006     }
1007
1008     return ret;
1009 }
1010
1011 Value listreceivedbyaddress(const Array& params, bool fHelp)
1012 {
1013     if (fHelp || params.size() > 2)
1014         throw runtime_error(
1015             "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1016             "[minconf] is the minimum number of confirmations before payments are included.\n"
1017             "[includeempty] whether to include addresses that haven't received any payments.\n"
1018             "Returns an array of objects containing:\n"
1019             "  \"address\" : receiving address\n"
1020             "  \"account\" : the account of the receiving address\n"
1021             "  \"amount\" : total amount received by the address\n"
1022             "  \"confirmations\" : number of confirmations of the most recent transaction included");
1023
1024     return ListReceived(params, false);
1025 }
1026
1027 Value listreceivedbyaccount(const Array& params, bool fHelp)
1028 {
1029     if (fHelp || params.size() > 2)
1030         throw runtime_error(
1031             "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1032             "[minconf] is the minimum number of confirmations before payments are included.\n"
1033             "[includeempty] whether to include accounts that haven't received any payments.\n"
1034             "Returns an array of objects containing:\n"
1035             "  \"account\" : the account of the receiving addresses\n"
1036             "  \"amount\" : total amount received by addresses with this account\n"
1037             "  \"confirmations\" : number of confirmations of the most recent transaction included");
1038
1039     return ListReceived(params, true);
1040 }
1041
1042 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1043 {
1044     CBitcoinAddress addr;
1045     if (addr.Set(dest))
1046         entry.push_back(Pair("address", addr.ToString()));
1047 }
1048
1049 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1050 {
1051     int64_t nGeneratedImmature, nGeneratedMature, nFee;
1052     string strSentAccount;
1053     list<pair<CTxDestination, int64_t> > listReceived;
1054     list<pair<CTxDestination, int64_t> > listSent;
1055
1056     wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1057
1058     bool fAllAccounts = (strAccount == string("*"));
1059     bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1060
1061     // Generated blocks assigned to account ""
1062     if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1063     {
1064         Object entry;
1065         entry.push_back(Pair("account", string("")));
1066         if (nGeneratedImmature)
1067         {
1068             entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1069             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1070         }
1071         else
1072         {
1073             entry.push_back(Pair("category", "generate"));
1074             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1075         }
1076         if (fLong)
1077             WalletTxToJSON(wtx, entry);
1078         ret.push_back(entry);
1079     }
1080
1081     // Sent
1082     if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1083     {
1084         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1085         {
1086             Object entry;
1087             entry.push_back(Pair("account", strSentAccount));
1088             if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1089                 entry.push_back(Pair("involvesWatchonly", true));
1090             MaybePushAddress(entry, s.first);
1091
1092             if (wtx.GetDepthInMainChain() < 0) {
1093                 entry.push_back(Pair("category", "conflicted"));
1094             } else {
1095                 entry.push_back(Pair("category", "send"));
1096             }
1097
1098             entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1099             entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1100             if (fLong)
1101                 WalletTxToJSON(wtx, entry);
1102             ret.push_back(entry);
1103         }
1104     }
1105
1106     // Received
1107     if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1108     {
1109         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1110         {
1111             string account;
1112             if (pwalletMain->mapAddressBook.count(r.first))
1113                 account = pwalletMain->mapAddressBook[r.first];
1114             if (fAllAccounts || (account == strAccount))
1115             {
1116                 Object entry;
1117                 entry.push_back(Pair("account", account));
1118                 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1119                     entry.push_back(Pair("involvesWatchonly", true));
1120                 MaybePushAddress(entry, r.first);
1121                 if (wtx.IsCoinBase())
1122                 {
1123                     if (wtx.GetDepthInMainChain() < 1)
1124                         entry.push_back(Pair("category", "orphan"));
1125                     else if (wtx.GetBlocksToMaturity() > 0)
1126                         entry.push_back(Pair("category", "immature"));
1127                     else
1128                         entry.push_back(Pair("category", "generate"));
1129                 }
1130                 else
1131                     entry.push_back(Pair("category", "receive"));
1132                 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1133                 if (fLong)
1134                     WalletTxToJSON(wtx, entry);
1135                 ret.push_back(entry);
1136             }
1137         }
1138     }
1139 }
1140
1141 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1142 {
1143     bool fAllAccounts = (strAccount == string("*"));
1144
1145     if (fAllAccounts || acentry.strAccount == strAccount)
1146     {
1147         Object entry;
1148         entry.push_back(Pair("account", acentry.strAccount));
1149         entry.push_back(Pair("category", "move"));
1150         entry.push_back(Pair("time", (int64_t)acentry.nTime));
1151         entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1152         entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1153         entry.push_back(Pair("comment", acentry.strComment));
1154         ret.push_back(entry);
1155     }
1156 }
1157
1158 Value listtransactions(const Array& params, bool fHelp)
1159 {
1160     if (fHelp || params.size() > 3)
1161         throw runtime_error(
1162             "listtransactions [account] [count=10] [from=0]\n"
1163             "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1164
1165     string strAccount = "*";
1166     if (params.size() > 0)
1167         strAccount = params[0].get_str();
1168     int nCount = 10;
1169     if (params.size() > 1)
1170         nCount = params[1].get_int();
1171     int nFrom = 0;
1172     if (params.size() > 2)
1173         nFrom = params[2].get_int();
1174
1175     isminefilter filter = MINE_SPENDABLE;
1176     if(params.size() > 3)
1177         if(params[3].get_bool())
1178             filter = filter | MINE_WATCH_ONLY;
1179
1180     if (nCount < 0)
1181         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1182     if (nFrom < 0)
1183         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1184
1185     Array ret;
1186
1187     std::list<CAccountingEntry> acentries;
1188     CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1189
1190     // iterate backwards until we have nCount items to return:
1191     for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1192     {
1193         CWalletTx *const pwtx = (*it).second.first;
1194         if (pwtx != 0)
1195             ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1196         CAccountingEntry *const pacentry = (*it).second.second;
1197         if (pacentry != 0)
1198             AcentryToJSON(*pacentry, strAccount, ret);
1199
1200         if ((int)ret.size() >= (nCount+nFrom)) break;
1201     }
1202     // ret is newest to oldest
1203
1204     if (nFrom > (int)ret.size())
1205         nFrom = ret.size();
1206     if ((nFrom + nCount) > (int)ret.size())
1207         nCount = ret.size() - nFrom;
1208     Array::iterator first = ret.begin();
1209     std::advance(first, nFrom);
1210     Array::iterator last = ret.begin();
1211     std::advance(last, nFrom+nCount);
1212
1213     if (last != ret.end()) ret.erase(last, ret.end());
1214     if (first != ret.begin()) ret.erase(ret.begin(), first);
1215
1216     std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1217
1218     return ret;
1219 }
1220
1221 Value listaccounts(const Array& params, bool fHelp)
1222 {
1223     if (fHelp || params.size() > 1)
1224         throw runtime_error(
1225             "listaccounts [minconf=1]\n"
1226             "Returns Object that has account names as keys, account balances as values.");
1227
1228     int nMinDepth = 1;
1229     if (params.size() > 0)
1230         nMinDepth = params[0].get_int();
1231
1232     isminefilter includeWatchonly = MINE_SPENDABLE;
1233     if(params.size() > 1)
1234         if(params[1].get_bool())
1235             includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1236
1237
1238     map<string, int64_t> mapAccountBalances;
1239     BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1240         if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1241             mapAccountBalances[entry.second] = 0;
1242     }
1243
1244     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1245     {
1246         const CWalletTx& wtx = (*it).second;
1247         int64_t nGeneratedImmature, nGeneratedMature, nFee;
1248         string strSentAccount;
1249         list<pair<CTxDestination, int64_t> > listReceived;
1250         list<pair<CTxDestination, int64_t> > listSent;
1251         wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1252         mapAccountBalances[strSentAccount] -= nFee;
1253         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1254             mapAccountBalances[strSentAccount] -= s.second;
1255         if (wtx.GetDepthInMainChain() >= nMinDepth)
1256         {
1257             mapAccountBalances[""] += nGeneratedMature;
1258             BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1259                 if (pwalletMain->mapAddressBook.count(r.first))
1260                     mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1261                 else
1262                     mapAccountBalances[""] += r.second;
1263         }
1264     }
1265
1266     list<CAccountingEntry> acentries;
1267     CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1268     BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1269         mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1270
1271     Object ret;
1272     BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) {
1273         ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1274     }
1275     return ret;
1276 }
1277
1278 Value listsinceblock(const Array& params, bool fHelp)
1279 {
1280     if (fHelp)
1281         throw runtime_error(
1282             "listsinceblock [blockhash] [target-confirmations]\n"
1283             "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1284
1285     CBlockIndex *pindex = NULL;
1286     int target_confirms = 1;
1287     isminefilter filter = MINE_SPENDABLE;
1288
1289     if (params.size() > 0)
1290     {
1291         uint256 blockId = 0;
1292
1293         blockId.SetHex(params[0].get_str());
1294         pindex = CBlockLocator(blockId).GetBlockIndex();
1295     }
1296
1297     if (params.size() > 1)
1298     {
1299         target_confirms = params[1].get_int();
1300
1301         if (target_confirms < 1)
1302             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1303     }
1304
1305     if(params.size() > 2)
1306         if(params[2].get_bool())
1307             filter = filter | MINE_WATCH_ONLY;
1308
1309     int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1310
1311     Array transactions;
1312
1313     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1314     {
1315         CWalletTx tx = (*it).second;
1316
1317         if (depth == -1 || tx.GetDepthInMainChain() < depth)
1318             ListTransactions(tx, "*", 0, true, transactions, filter);
1319     }
1320
1321     uint256 lastblock;
1322
1323     if (target_confirms == 1)
1324     {
1325         lastblock = hashBestChain;
1326     }
1327     else
1328     {
1329         int target_height = pindexBest->nHeight + 1 - target_confirms;
1330
1331         CBlockIndex *block;
1332         for (block = pindexBest;
1333              block && block->nHeight > target_height;
1334              block = block->pprev)  { }
1335
1336         lastblock = block ? block->GetBlockHash() : 0;
1337     }
1338
1339     Object ret;
1340     ret.push_back(Pair("transactions", transactions));
1341     ret.push_back(Pair("lastblock", lastblock.GetHex()));
1342
1343     return ret;
1344 }
1345
1346 Value gettransaction(const Array& params, bool fHelp)
1347 {
1348     if (fHelp || params.size() != 1)
1349         throw runtime_error(
1350             "gettransaction <txid>\n"
1351             "Get detailed information about <txid>");
1352
1353     uint256 hash;
1354     hash.SetHex(params[0].get_str());
1355
1356     isminefilter filter = MINE_SPENDABLE;
1357     if(params.size() > 1)
1358         if(params[1].get_bool())
1359             filter = filter | MINE_WATCH_ONLY;
1360
1361     Object entry;
1362
1363     if (pwalletMain->mapWallet.count(hash))
1364     {
1365         const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1366
1367         TxToJSON(wtx, 0, entry);
1368
1369         int64_t nCredit = wtx.GetCredit(filter);
1370         int64_t nDebit = wtx.GetDebit(filter);
1371         int64_t nNet = nCredit - nDebit;
1372         int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1373
1374         entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1375         if (wtx.IsFromMe(filter))
1376             entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1377
1378         WalletTxToJSON(wtx, entry);
1379
1380         Array details;
1381         ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1382         entry.push_back(Pair("details", details));
1383     }
1384     else
1385     {
1386         CTransaction tx;
1387         uint256 hashBlock = 0;
1388         if (GetTransaction(hash, tx, hashBlock))
1389         {
1390             TxToJSON(tx, 0, entry);
1391             if (hashBlock == 0)
1392                 entry.push_back(Pair("confirmations", 0));
1393             else
1394             {
1395                 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1396                 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1397                 if (mi != mapBlockIndex.end() && (*mi).second)
1398                 {
1399                     CBlockIndex* pindex = (*mi).second;
1400                     if (pindex->IsInMainChain())
1401                         entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1402                     else
1403                         entry.push_back(Pair("confirmations", 0));
1404                 }
1405             }
1406         }
1407         else
1408             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1409     }
1410
1411     return entry;
1412 }
1413
1414
1415 Value backupwallet(const Array& params, bool fHelp)
1416 {
1417     if (fHelp || params.size() != 1)
1418         throw runtime_error(
1419             "backupwallet <destination>\n"
1420             "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1421
1422     string strDest = params[0].get_str();
1423     if (!BackupWallet(*pwalletMain, strDest))
1424         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1425
1426     return Value::null;
1427 }
1428
1429
1430 Value keypoolrefill(const Array& params, bool fHelp)
1431 {
1432     if (fHelp || params.size() > 1)
1433         throw runtime_error(
1434             "keypoolrefill [new-size]\n"
1435             "Fills the keypool.\n"
1436             "IMPORTANT: Any previous backups you have made of your wallet file "
1437             "should be replaced with the newly generated one."
1438             + HelpRequiringPassphrase());
1439
1440     unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1441     if (params.size() > 0) {
1442         if (params[0].get_int() < 0)
1443             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1444         nSize = (unsigned int) params[0].get_int();
1445     }
1446
1447     EnsureWalletIsUnlocked();
1448
1449     pwalletMain->TopUpKeyPool(nSize);
1450
1451     if (pwalletMain->GetKeyPoolSize() < nSize)
1452         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1453
1454     return Value::null;
1455 }
1456
1457 Value keypoolreset(const Array& params, bool fHelp)
1458 {
1459     if (fHelp || params.size() > 1)
1460         throw runtime_error(
1461             "keypoolreset [new-size]\n"
1462             "Resets the keypool.\n"
1463             "IMPORTANT: Any previous backups you have made of your wallet file "
1464             "should be replaced with the newly generated one."
1465             + HelpRequiringPassphrase());
1466
1467     unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1468     if (params.size() > 0) {
1469         if (params[0].get_int() < 0)
1470             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1471         nSize = (unsigned int) params[0].get_int();
1472     }
1473
1474     EnsureWalletIsUnlocked();
1475
1476     pwalletMain->NewKeyPool(nSize);
1477
1478     if (pwalletMain->GetKeyPoolSize() < nSize)
1479         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1480
1481     return Value::null;
1482 }
1483
1484
1485 void ThreadTopUpKeyPool(void* parg)
1486 {
1487     // Make this thread recognisable as the key-topping-up thread
1488     RenameThread("novacoin-key-top");
1489
1490     pwalletMain->TopUpKeyPool();
1491 }
1492
1493 void ThreadCleanWalletPassphrase(void* parg)
1494 {
1495     // Make this thread recognisable as the wallet relocking thread
1496     RenameThread("novacoin-lock-wa");
1497
1498     int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1499
1500     ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1501
1502     if (nWalletUnlockTime == 0)
1503     {
1504         nWalletUnlockTime = nMyWakeTime;
1505
1506         do
1507         {
1508             if (nWalletUnlockTime==0)
1509                 break;
1510             int64_t nToSleep = nWalletUnlockTime - GetTimeMillis();
1511             if (nToSleep <= 0)
1512                 break;
1513
1514             LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1515             Sleep(nToSleep);
1516             ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1517
1518         } while(1);
1519
1520         if (nWalletUnlockTime)
1521         {
1522             nWalletUnlockTime = 0;
1523             pwalletMain->Lock();
1524         }
1525     }
1526     else
1527     {
1528         if (nWalletUnlockTime < nMyWakeTime)
1529             nWalletUnlockTime = nMyWakeTime;
1530     }
1531
1532     LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1533
1534     delete (int64_t*)parg;
1535 }
1536
1537 Value walletpassphrase(const Array& params, bool fHelp)
1538 {
1539     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1540         throw runtime_error(
1541             "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1542             "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1543             "mintonly is optional true/false allowing only block minting.");
1544     if (fHelp)
1545         return true;
1546     if (!pwalletMain->IsCrypted())
1547         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1548
1549     if (!pwalletMain->IsLocked())
1550         throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1551     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1552     SecureString strWalletPass;
1553     strWalletPass.reserve(100);
1554     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1555     // Alternately, find a way to make params[0] mlock()'d to begin with.
1556     strWalletPass = params[0].get_str().c_str();
1557
1558     if (strWalletPass.length() > 0)
1559     {
1560         if (!pwalletMain->Unlock(strWalletPass))
1561             throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1562     }
1563     else
1564         throw runtime_error(
1565             "walletpassphrase <passphrase> <timeout>\n"
1566             "Stores the wallet decryption key in memory for <timeout> seconds.");
1567
1568     NewThread(ThreadTopUpKeyPool, NULL);
1569     int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1570     NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1571
1572     // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1573     if (params.size() > 2)
1574         fWalletUnlockMintOnly = params[2].get_bool();
1575     else
1576         fWalletUnlockMintOnly = false;
1577
1578     return Value::null;
1579 }
1580
1581
1582 Value walletpassphrasechange(const Array& params, bool fHelp)
1583 {
1584     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1585         throw runtime_error(
1586             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1587             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1588     if (fHelp)
1589         return true;
1590     if (!pwalletMain->IsCrypted())
1591         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1592
1593     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1594     // Alternately, find a way to make params[0] mlock()'d to begin with.
1595     SecureString strOldWalletPass;
1596     strOldWalletPass.reserve(100);
1597     strOldWalletPass = params[0].get_str().c_str();
1598
1599     SecureString strNewWalletPass;
1600     strNewWalletPass.reserve(100);
1601     strNewWalletPass = params[1].get_str().c_str();
1602
1603     if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1604         throw runtime_error(
1605             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1606             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1607
1608     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1609         throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1610
1611     return Value::null;
1612 }
1613
1614
1615 Value walletlock(const Array& params, bool fHelp)
1616 {
1617     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1618         throw runtime_error(
1619             "walletlock\n"
1620             "Removes the wallet encryption key from memory, locking the wallet.\n"
1621             "After calling this method, you will need to call walletpassphrase again\n"
1622             "before being able to call any methods which require the wallet to be unlocked.");
1623     if (fHelp)
1624         return true;
1625     if (!pwalletMain->IsCrypted())
1626         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1627
1628     {
1629         LOCK(cs_nWalletUnlockTime);
1630         pwalletMain->Lock();
1631         nWalletUnlockTime = 0;
1632     }
1633
1634     return Value::null;
1635 }
1636
1637
1638 Value encryptwallet(const Array& params, bool fHelp)
1639 {
1640     if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1641         throw runtime_error(
1642             "encryptwallet <passphrase>\n"
1643             "Encrypts the wallet with <passphrase>.");
1644     if (fHelp)
1645         return true;
1646     if (pwalletMain->IsCrypted())
1647         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1648
1649     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1650     // Alternately, find a way to make params[0] mlock()'d to begin with.
1651     SecureString strWalletPass;
1652     strWalletPass.reserve(100);
1653     strWalletPass = params[0].get_str().c_str();
1654
1655     if (strWalletPass.length() < 1)
1656         throw runtime_error(
1657             "encryptwallet <passphrase>\n"
1658             "Encrypts the wallet with <passphrase>.");
1659
1660     if (!pwalletMain->EncryptWallet(strWalletPass))
1661         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1662
1663     // BDB seems to have a bad habit of writing old data into
1664     // slack space in .dat files; that is bad if the old data is
1665     // unencrypted private keys. So:
1666     StartShutdown();
1667     return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet.  The keypool has been flushed, you need to make a new backup.";
1668 }
1669
1670 class DescribeAddressVisitor : public boost::static_visitor<Object>
1671 {
1672 private:
1673     isminetype mine;
1674 public:
1675     DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1676
1677     Object operator()(const CNoDestination &dest) const { return Object(); }
1678     Object operator()(const CKeyID &keyID) const {
1679         Object obj;
1680         CPubKey vchPubKey;
1681         pwalletMain->GetPubKey(keyID, vchPubKey);
1682         obj.push_back(Pair("isscript", false));
1683         if (mine == MINE_SPENDABLE) {
1684             pwalletMain->GetPubKey(keyID, vchPubKey);
1685             obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1686             obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1687         }
1688         return obj;
1689     }
1690
1691     Object operator()(const CScriptID &scriptID) const {
1692         Object obj;
1693         obj.push_back(Pair("isscript", true));
1694         if (mine == MINE_SPENDABLE) {
1695             CScript subscript;
1696             pwalletMain->GetCScript(scriptID, subscript);
1697             std::vector<CTxDestination> addresses;
1698             txnouttype whichType;
1699             int nRequired;
1700             ExtractDestinations(subscript, whichType, addresses, nRequired);
1701             obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1702             obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1703             Array a;
1704             BOOST_FOREACH(const CTxDestination& addr, addresses)
1705                 a.push_back(CBitcoinAddress(addr).ToString());
1706             obj.push_back(Pair("addresses", a));
1707             if (whichType == TX_MULTISIG)
1708                 obj.push_back(Pair("sigsrequired", nRequired));
1709         }
1710         return obj;
1711     }
1712 };
1713
1714 Value validateaddress(const Array& params, bool fHelp)
1715 {
1716     if (fHelp || params.size() != 1)
1717         throw runtime_error(
1718             "validateaddress <novacoinaddress>\n"
1719             "Return information about <novacoinaddress>.");
1720
1721     CBitcoinAddress address(params[0].get_str());
1722     bool isValid = address.IsValid();
1723
1724     Object ret;
1725     ret.push_back(Pair("isvalid", isValid));
1726     if (isValid)
1727     {
1728         CTxDestination dest = address.Get();
1729         string currentAddress = address.ToString();
1730         ret.push_back(Pair("address", currentAddress));
1731         isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1732         ret.push_back(Pair("ismine", mine != MINE_NO));
1733         if (mine != MINE_NO) {
1734             ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1735             Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1736             ret.insert(ret.end(), detail.begin(), detail.end());
1737         }
1738         if (pwalletMain->mapAddressBook.count(dest))
1739             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1740     }
1741     return ret;
1742 }
1743
1744 // ppcoin: reserve balance from being staked for network protection
1745 Value reservebalance(const Array& params, bool fHelp)
1746 {
1747     if (fHelp || params.size() > 2)
1748         throw runtime_error(
1749             "reservebalance [<reserve> [amount]]\n"
1750             "<reserve> is true or false to turn balance reserve on or off.\n"
1751             "<amount> is a real and rounded to cent.\n"
1752             "Set reserve amount not participating in network protection.\n"
1753             "If no parameters provided current setting is printed.\n");
1754
1755     if (params.size() > 0)
1756     {
1757         bool fReserve = params[0].get_bool();
1758         if (fReserve)
1759         {
1760             if (params.size() == 1)
1761                 throw runtime_error("must provide amount to reserve balance.\n");
1762             int64_t nAmount = AmountFromValue(params[1]);
1763             nAmount = (nAmount / CENT) * CENT;  // round to cent
1764             if (nAmount < 0)
1765                 throw runtime_error("amount cannot be negative.\n");
1766             mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1767         }
1768         else
1769         {
1770             if (params.size() > 1)
1771                 throw runtime_error("cannot specify amount to turn off reserve.\n");
1772             mapArgs["-reservebalance"] = "0";
1773         }
1774     }
1775
1776     Object result;
1777     if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1778         throw runtime_error("invalid reserve balance amount\n");
1779     result.push_back(Pair("reserve", (nReserveBalance > 0)));
1780     result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1781     return result;
1782 }
1783
1784
1785 // ppcoin: check wallet integrity
1786 Value checkwallet(const Array& params, bool fHelp)
1787 {
1788     if (fHelp || params.size() > 0)
1789         throw runtime_error(
1790             "checkwallet\n"
1791             "Check wallet for integrity.\n");
1792
1793     int nMismatchSpent;
1794     int64_t nBalanceInQuestion;
1795     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1796     Object result;
1797     if (nMismatchSpent == 0)
1798         result.push_back(Pair("wallet check passed", true));
1799     else
1800     {
1801         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1802         result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1803     }
1804     return result;
1805 }
1806
1807
1808 // ppcoin: repair wallet
1809 Value repairwallet(const Array& params, bool fHelp)
1810 {
1811     if (fHelp || params.size() > 0)
1812         throw runtime_error(
1813             "repairwallet\n"
1814             "Repair wallet if checkwallet reports any problem.\n");
1815
1816     int nMismatchSpent;
1817     int64_t nBalanceInQuestion;
1818     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1819     Object result;
1820     if (nMismatchSpent == 0)
1821         result.push_back(Pair("wallet check passed", true));
1822     else
1823     {
1824         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1825         result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1826     }
1827     return result;
1828 }
1829
1830 // NovaCoin: resend unconfirmed wallet transactions
1831 Value resendtx(const Array& params, bool fHelp)
1832 {
1833     if (fHelp || params.size() > 1)
1834         throw runtime_error(
1835             "resendtx\n"
1836             "Re-send unconfirmed transactions.\n"
1837         );
1838
1839     ResendWalletTransactions();
1840
1841     return Value::null;
1842 }
1843
1844 // Make a public-private key pair
1845 Value makekeypair(const Array& params, bool fHelp)
1846 {
1847     if (fHelp || params.size() > 0)
1848         throw runtime_error(
1849             "makekeypair\n"
1850             "Make a public/private key pair.\n");
1851
1852     string strPrefix = "";
1853     if (params.size() > 0)
1854         strPrefix = params[0].get_str();
1855
1856     CKey key;
1857     key.MakeNewKey(true);
1858
1859     CPrivKey vchPrivKey = key.GetPrivKey();
1860     Object result;
1861     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1862
1863     bool fCompressed;
1864     CSecret vchSecret = key.GetSecret(fCompressed);
1865     result.push_back(Pair("Secret", HexStr<CSecret::iterator>(vchSecret.begin(), vchSecret.end())));
1866     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
1867     return result;
1868 }
1869
1870 Value newmalleablekey(const Array& params, bool fHelp)
1871 {
1872     if (fHelp || params.size() > 0)
1873         throw runtime_error(
1874             "newmalleablekey\n"
1875             "Make a malleable public/private key pair.\n");
1876
1877     if (!fTestNet)
1878         throw runtime_error("This feature has been disabled for mainNet clients");
1879
1880     CMalleableKeyView keyView = pwalletMain->GenerateNewMalleableKey();
1881
1882     CMalleableKey mKey;
1883     if (!pwalletMain->GetMalleableKey(keyView, mKey))
1884         throw runtime_error("Unable to generate new malleable key");
1885
1886     Object result;
1887     result.push_back(Pair("PublicPair", mKey.GetMalleablePubKey().ToString()));
1888     result.push_back(Pair("KeyView", keyView.ToString()));
1889
1890     return result;
1891 }
1892
1893 Value dumpmalleablekey(const Array& params, bool fHelp)
1894 {
1895     if (fHelp || params.size() != 1)
1896         throw runtime_error (
1897             "dumpmalleablekey <Key view>\n"
1898             "Dump the private and public key pairs, which correspond to provided key view.\n");
1899
1900     CMalleableKey mKey;
1901     CMalleableKeyView keyView;
1902     keyView.SetString(params[0].get_str());
1903
1904     if (!pwalletMain->GetMalleableKey(keyView, mKey))
1905         throw runtime_error("There is no such item in the wallet");
1906
1907     Object result;
1908     result.push_back(Pair("PrivatePair", mKey.ToString()));
1909     result.push_back(Pair("PublicPair", mKey.GetMalleablePubKey().ToString()));
1910
1911     return result;
1912 }
1913
1914 Value adjustmalleablekey(const Array& params, bool fHelp)
1915 {
1916     if (fHelp || params.size() != 3)
1917         throw runtime_error(
1918             "adjustmalleablekey <Malleable key data> <Public key variant data> <R data>\n"
1919             "Calculate new private key using provided malleable key, public key and R data.\n");
1920
1921     CMalleableKey malleableKey;
1922     malleableKey.SetString(params[0].get_str());
1923
1924     CKey privKeyVariant;
1925     CPubKey vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
1926
1927     CPubKey R(ParseHex(params[2].get_str()));
1928
1929     if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) {
1930         throw runtime_error("Unable to calculate the private key");
1931     }
1932
1933     Object result;
1934     bool fCompressed;
1935     CSecret vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed);
1936
1937     result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString()));
1938
1939     return result;
1940 }
1941
1942 Value adjustmalleablepubkey(const Array& params, bool fHelp)
1943 {
1944     if (fHelp || params.size() > 2 || params.size() == 0)
1945         throw runtime_error(
1946             "adjustmalleablepubkey <Malleable public key data>\n"
1947             "Calculate new public key using provided malleable public key data.\n");
1948
1949     string pubKeyPair = params[0].get_str();
1950     CMalleablePubKey malleablePubKey;
1951
1952     if (pubKeyPair.size() == 138) {
1953         CDataStream ssPublicBytes(ParseHex(pubKeyPair), SER_NETWORK, PROTOCOL_VERSION);
1954         ssPublicBytes >> malleablePubKey;
1955     } else
1956         malleablePubKey.SetString(pubKeyPair);
1957
1958     CPubKey R, vchPubKeyVariant;
1959     malleablePubKey.GetVariant(R, vchPubKeyVariant);
1960
1961     Object result;
1962     result.push_back(Pair("R", HexStr(R.Raw())));
1963     result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.Raw())));
1964     result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
1965
1966     return result;
1967 }
1968
1969 Value listmalleableviews(const Array& params, bool fHelp)
1970 {
1971     if (fHelp || params.size() != 0)
1972         throw runtime_error(
1973             "listmalleableviews\n"
1974             "Get list of views for generated malleable keys.\n");
1975
1976     std::list<CMalleableKeyView> keyViewList;
1977     pwalletMain->ListMalleableViews(keyViewList);
1978
1979     Array result;
1980     BOOST_FOREACH(const CMalleableKeyView &keyView, keyViewList)
1981     {
1982         result.push_back(keyView.ToString());
1983     }
1984
1985     return result;
1986 }
1987