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