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