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