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