P2SH related RPC improvements
[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",   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() > 0)
1341         throw runtime_error(
1342             "keypoolrefill\n"
1343             "Fills the keypool."
1344             + HelpRequiringPassphrase());
1345
1346     EnsureWalletIsUnlocked();
1347
1348     pwalletMain->TopUpKeyPool();
1349
1350     if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1351         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1352
1353     return Value::null;
1354 }
1355
1356
1357 void ThreadTopUpKeyPool(void* parg)
1358 {
1359     // Make this thread recognisable as the key-topping-up thread
1360     RenameThread("bitcoin-key-top");
1361
1362     pwalletMain->TopUpKeyPool();
1363 }
1364
1365 void ThreadCleanWalletPassphrase(void* parg)
1366 {
1367     // Make this thread recognisable as the wallet relocking thread
1368     RenameThread("bitcoin-lock-wa");
1369
1370     int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1371
1372     ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1373
1374     if (nWalletUnlockTime == 0)
1375     {
1376         nWalletUnlockTime = nMyWakeTime;
1377
1378         do
1379         {
1380             if (nWalletUnlockTime==0)
1381                 break;
1382             int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1383             if (nToSleep <= 0)
1384                 break;
1385
1386             LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1387             Sleep(nToSleep);
1388             ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1389
1390         } while(1);
1391
1392         if (nWalletUnlockTime)
1393         {
1394             nWalletUnlockTime = 0;
1395             pwalletMain->Lock();
1396         }
1397     }
1398     else
1399     {
1400         if (nWalletUnlockTime < nMyWakeTime)
1401             nWalletUnlockTime = nMyWakeTime;
1402     }
1403
1404     LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1405
1406     delete (int64*)parg;
1407 }
1408
1409 Value walletpassphrase(const Array& params, bool fHelp)
1410 {
1411     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1412         throw runtime_error(
1413             "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1414             "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1415             "mintonly is optional true/false allowing only block minting.");
1416     if (fHelp)
1417         return true;
1418     if (!pwalletMain->IsCrypted())
1419         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1420
1421     if (!pwalletMain->IsLocked())
1422         throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1423     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1424     SecureString strWalletPass;
1425     strWalletPass.reserve(100);
1426     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1427     // Alternately, find a way to make params[0] mlock()'d to begin with.
1428     strWalletPass = params[0].get_str().c_str();
1429
1430     if (strWalletPass.length() > 0)
1431     {
1432         if (!pwalletMain->Unlock(strWalletPass))
1433             throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1434     }
1435     else
1436         throw runtime_error(
1437             "walletpassphrase <passphrase> <timeout>\n"
1438             "Stores the wallet decryption key in memory for <timeout> seconds.");
1439
1440     NewThread(ThreadTopUpKeyPool, NULL);
1441     int64* pnSleepTime = new int64(params[1].get_int64());
1442     NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1443
1444     // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1445     if (params.size() > 2)
1446         fWalletUnlockMintOnly = params[2].get_bool();
1447     else
1448         fWalletUnlockMintOnly = false;
1449
1450     return Value::null;
1451 }
1452
1453
1454 Value walletpassphrasechange(const Array& params, bool fHelp)
1455 {
1456     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1457         throw runtime_error(
1458             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1459             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1460     if (fHelp)
1461         return true;
1462     if (!pwalletMain->IsCrypted())
1463         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1464
1465     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1466     // Alternately, find a way to make params[0] mlock()'d to begin with.
1467     SecureString strOldWalletPass;
1468     strOldWalletPass.reserve(100);
1469     strOldWalletPass = params[0].get_str().c_str();
1470
1471     SecureString strNewWalletPass;
1472     strNewWalletPass.reserve(100);
1473     strNewWalletPass = params[1].get_str().c_str();
1474
1475     if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1476         throw runtime_error(
1477             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1478             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1479
1480     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1481         throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1482
1483     return Value::null;
1484 }
1485
1486
1487 Value walletlock(const Array& params, bool fHelp)
1488 {
1489     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1490         throw runtime_error(
1491             "walletlock\n"
1492             "Removes the wallet encryption key from memory, locking the wallet.\n"
1493             "After calling this method, you will need to call walletpassphrase again\n"
1494             "before being able to call any methods which require the wallet to be unlocked.");
1495     if (fHelp)
1496         return true;
1497     if (!pwalletMain->IsCrypted())
1498         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1499
1500     {
1501         LOCK(cs_nWalletUnlockTime);
1502         pwalletMain->Lock();
1503         nWalletUnlockTime = 0;
1504     }
1505
1506     return Value::null;
1507 }
1508
1509
1510 Value encryptwallet(const Array& params, bool fHelp)
1511 {
1512     if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1513         throw runtime_error(
1514             "encryptwallet <passphrase>\n"
1515             "Encrypts the wallet with <passphrase>.");
1516     if (fHelp)
1517         return true;
1518     if (pwalletMain->IsCrypted())
1519         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1520
1521     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1522     // Alternately, find a way to make params[0] mlock()'d to begin with.
1523     SecureString strWalletPass;
1524     strWalletPass.reserve(100);
1525     strWalletPass = params[0].get_str().c_str();
1526
1527     if (strWalletPass.length() < 1)
1528         throw runtime_error(
1529             "encryptwallet <passphrase>\n"
1530             "Encrypts the wallet with <passphrase>.");
1531
1532     if (!pwalletMain->EncryptWallet(strWalletPass))
1533         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1534
1535     // BDB seems to have a bad habit of writing old data into
1536     // slack space in .dat files; that is bad if the old data is
1537     // unencrypted private keys. So:
1538     StartShutdown();
1539     return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet.  The keypool has been flushed, you need to make a new backup.";
1540 }
1541
1542 class DescribeAddressVisitor : public boost::static_visitor<Object>
1543 {
1544 public:
1545     Object operator()(const CNoDestination &dest) const { return Object(); }
1546
1547     Object operator()(const CKeyID &keyID) const {
1548         Object obj;
1549         CPubKey vchPubKey;
1550         pwalletMain->GetPubKey(keyID, vchPubKey);
1551         obj.push_back(Pair("isscript", false));
1552         obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1553         obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1554         return obj;
1555     }
1556
1557     Object operator()(const CScriptID &scriptID) const {
1558         Object obj;
1559         obj.push_back(Pair("isscript", true));
1560         CScript subscript;
1561         pwalletMain->GetCScript(scriptID, subscript);
1562         std::vector<CTxDestination> addresses;
1563         txnouttype whichType;
1564         int nRequired;
1565         ExtractDestinations(subscript, whichType, addresses, nRequired);
1566         obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1567         obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1568         Array a;
1569         BOOST_FOREACH(const CTxDestination& addr, addresses)
1570             a.push_back(CBitcoinAddress(addr).ToString());
1571         obj.push_back(Pair("addresses", a));
1572         if (whichType == TX_MULTISIG)
1573             obj.push_back(Pair("sigsrequired", nRequired));
1574         return obj;
1575     }
1576 };
1577
1578 Value validateaddress(const Array& params, bool fHelp)
1579 {
1580     if (fHelp || params.size() != 1)
1581         throw runtime_error(
1582             "validateaddress <novacoinaddress>\n"
1583             "Return information about <novacoinaddress>.");
1584
1585     CBitcoinAddress address(params[0].get_str());
1586     bool isValid = address.IsValid();
1587
1588     Object ret;
1589     ret.push_back(Pair("isvalid", isValid));
1590     if (isValid)
1591     {
1592         CTxDestination dest = address.Get();
1593         string currentAddress = address.ToString();
1594         ret.push_back(Pair("address", currentAddress));
1595         bool fMine = IsMine(*pwalletMain, dest);
1596         ret.push_back(Pair("ismine", fMine));
1597         if (fMine) {
1598             Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1599             ret.insert(ret.end(), detail.begin(), detail.end());
1600         }
1601         if (pwalletMain->mapAddressBook.count(dest))
1602             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1603     }
1604     return ret;
1605 }
1606
1607 Value validatepubkey(const Array& params, bool fHelp)
1608 {
1609     if (fHelp || !params.size() || params.size() > 2)
1610         throw runtime_error(
1611             "validatepubkey <novacoinpubkey>\n"
1612             "Return information about <novacoinpubkey>.");
1613
1614     std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1615     CPubKey pubKey(vchPubKey);
1616
1617     bool isValid = pubKey.IsValid();
1618     bool isCompressed = pubKey.IsCompressed();
1619     CKeyID keyID = pubKey.GetID();
1620
1621     CBitcoinAddress address;
1622     address.Set(keyID);
1623
1624     Object ret;
1625     ret.push_back(Pair("isvalid", isValid));
1626     if (isValid)
1627     {
1628         CTxDestination dest = address.Get();
1629         string currentAddress = address.ToString();
1630         ret.push_back(Pair("address", currentAddress));
1631         bool fMine = IsMine(*pwalletMain, dest);
1632         ret.push_back(Pair("ismine", fMine));
1633         ret.push_back(Pair("iscompressed", isCompressed));
1634         if (fMine) {
1635             Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1636             ret.insert(ret.end(), detail.begin(), detail.end());
1637         }
1638         if (pwalletMain->mapAddressBook.count(dest))
1639             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1640     }
1641     return ret;
1642 }
1643
1644 // ppcoin: reserve balance from being staked for network protection
1645 Value reservebalance(const Array& params, bool fHelp)
1646 {
1647     if (fHelp || params.size() > 2)
1648         throw runtime_error(
1649             "reservebalance [<reserve> [amount]]\n"
1650             "<reserve> is true or false to turn balance reserve on or off.\n"
1651             "<amount> is a real and rounded to cent.\n"
1652             "Set reserve amount not participating in network protection.\n"
1653             "If no parameters provided current setting is printed.\n");
1654
1655     if (params.size() > 0)
1656     {
1657         bool fReserve = params[0].get_bool();
1658         if (fReserve)
1659         {
1660             if (params.size() == 1)
1661                 throw runtime_error("must provide amount to reserve balance.\n");
1662             int64 nAmount = AmountFromValue(params[1]);
1663             nAmount = (nAmount / CENT) * CENT;  // round to cent
1664             if (nAmount < 0)
1665                 throw runtime_error("amount cannot be negative.\n");
1666             mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1667         }
1668         else
1669         {
1670             if (params.size() > 1)
1671                 throw runtime_error("cannot specify amount to turn off reserve.\n");
1672             mapArgs["-reservebalance"] = "0";
1673         }
1674     }
1675
1676     Object result;
1677     int64 nReserveBalance = 0;
1678     if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1679         throw runtime_error("invalid reserve balance amount\n");
1680     result.push_back(Pair("reserve", (nReserveBalance > 0)));
1681     result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1682     return result;
1683 }
1684
1685
1686 // ppcoin: check wallet integrity
1687 Value checkwallet(const Array& params, bool fHelp)
1688 {
1689     if (fHelp || params.size() > 0)
1690         throw runtime_error(
1691             "checkwallet\n"
1692             "Check wallet for integrity.\n");
1693
1694     int nMismatchSpent;
1695     int64 nBalanceInQuestion;
1696     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1697     Object result;
1698     if (nMismatchSpent == 0)
1699         result.push_back(Pair("wallet check passed", true));
1700     else
1701     {
1702         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1703         result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1704     }
1705     return result;
1706 }
1707
1708
1709 // ppcoin: repair wallet
1710 Value repairwallet(const Array& params, bool fHelp)
1711 {
1712     if (fHelp || params.size() > 0)
1713         throw runtime_error(
1714             "repairwallet\n"
1715             "Repair wallet if checkwallet reports any problem.\n");
1716
1717     int nMismatchSpent;
1718     int64 nBalanceInQuestion;
1719     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1720     Object result;
1721     if (nMismatchSpent == 0)
1722         result.push_back(Pair("wallet check passed", true));
1723     else
1724     {
1725         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1726         result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1727     }
1728     return result;
1729 }
1730
1731 // NovaCoin: resend unconfirmed wallet transactions
1732 Value resendtx(const Array& params, bool fHelp)
1733 {
1734     if (fHelp || params.size() > 1)
1735         throw runtime_error(
1736             "resendtx\n"
1737             "Re-send unconfirmed transactions.\n"
1738         );
1739
1740     ResendWalletTransactions();
1741
1742     return Value::null;
1743 }
1744
1745 // ppcoin: make a public-private key pair
1746 Value makekeypair(const Array& params, bool fHelp)
1747 {
1748     if (fHelp || params.size() > 1)
1749         throw runtime_error(
1750             "makekeypair [prefix]\n"
1751             "Make a public/private key pair.\n"
1752             "[prefix] is optional preferred prefix for the public key.\n");
1753
1754     string strPrefix = "";
1755     if (params.size() > 0)
1756         strPrefix = params[0].get_str();
1757  
1758     CKey key;
1759     key.MakeNewKey(false);
1760
1761     CPrivKey vchPrivKey = key.GetPrivKey();
1762     Object result;
1763     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1764     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
1765     return result;
1766 }