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