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