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