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