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