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