8558036db62cb2c0e0d760bebbdb28cc4bfb1bcf
[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->GetWatchOnlyBalance())));
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, const isminefilter& filter)
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, filter);
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, const isminefilter& filter)
510 {
511     CWalletDB walletdb(pwalletMain->strWalletFile);
512     return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
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] [watchonly=0]\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.\n"
523             "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
524
525     if (params.size() == 0)
526         return  ValueFromAmount(pwalletMain->GetBalance());
527
528     int nMinDepth = 1;
529     if (params.size() > 1)
530         nMinDepth = params[1].get_int();
531     isminefilter filter = MINE_SPENDABLE;
532     if(params.size() > 2)
533         if(params[2].get_bool())
534             filter = filter | MINE_WATCH_ONLY;
535
536     if (params[0].get_str() == "*") {
537         // Calculate total balance a different way from GetBalance()
538         // (GetBalance() sums up all unspent TxOuts)
539         // getbalance and getbalance '*' 0 should return the same number.
540         int64 nBalance = 0;
541         for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
542         {
543             const CWalletTx& wtx = (*it).second;
544             if (!wtx.IsTrusted())
545                 continue;
546
547             int64 allGeneratedImmature, allGeneratedMature, allFee;
548             allGeneratedImmature = allGeneratedMature = allFee = 0;
549
550             string strSentAccount;
551             list<pair<CTxDestination, int64> > listReceived;
552             list<pair<CTxDestination, int64> > listSent;
553             wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
554             if (wtx.GetDepthInMainChain() >= nMinDepth)
555             {
556                 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
557                     nBalance += r.second;
558             }
559             BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
560                 nBalance -= r.second;
561             nBalance -= allFee;
562             nBalance += allGeneratedMature;
563         }
564         return  ValueFromAmount(nBalance);
565     }
566
567     string strAccount = AccountFromValue(params[0]);
568
569     int64 nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
570
571     return ValueFromAmount(nBalance);
572 }
573
574
575 Value movecmd(const Array& params, bool fHelp)
576 {
577     if (fHelp || params.size() < 3 || params.size() > 5)
578         throw runtime_error(
579             "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
580             "Move from one account in your wallet to another.");
581
582     string strFrom = AccountFromValue(params[0]);
583     string strTo = AccountFromValue(params[1]);
584     int64 nAmount = AmountFromValue(params[2]);
585
586     if (nAmount < MIN_TXOUT_AMOUNT)
587         throw JSONRPCError(-101, "Send amount too small");
588
589     if (params.size() > 3)
590         // unused parameter, used to be nMinDepth, keep type-checking it though
591         (void)params[3].get_int();
592     string strComment;
593     if (params.size() > 4)
594         strComment = params[4].get_str();
595
596     CWalletDB walletdb(pwalletMain->strWalletFile);
597     if (!walletdb.TxnBegin())
598         throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
599
600     int64 nNow = GetAdjustedTime();
601
602     // Debit
603     CAccountingEntry debit;
604     debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
605     debit.strAccount = strFrom;
606     debit.nCreditDebit = -nAmount;
607     debit.nTime = nNow;
608     debit.strOtherAccount = strTo;
609     debit.strComment = strComment;
610     walletdb.WriteAccountingEntry(debit);
611
612     // Credit
613     CAccountingEntry credit;
614     credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
615     credit.strAccount = strTo;
616     credit.nCreditDebit = nAmount;
617     credit.nTime = nNow;
618     credit.strOtherAccount = strFrom;
619     credit.strComment = strComment;
620     walletdb.WriteAccountingEntry(credit);
621
622     if (!walletdb.TxnCommit())
623         throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
624
625     return true;
626 }
627
628
629 Value sendfrom(const Array& params, bool fHelp)
630 {
631     if (fHelp || params.size() < 3 || params.size() > 6)
632         throw runtime_error(
633             "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
634             "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
635             + HelpRequiringPassphrase());
636
637     string strAccount = AccountFromValue(params[0]);
638     CBitcoinAddress address(params[1].get_str());
639     if (!address.IsValid())
640         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
641     int64 nAmount = AmountFromValue(params[2]);
642
643     if (nAmount < MIN_TXOUT_AMOUNT)
644         throw JSONRPCError(-101, "Send amount too small");
645
646     int nMinDepth = 1;
647     if (params.size() > 3)
648         nMinDepth = params[3].get_int();
649
650     CWalletTx wtx;
651     wtx.strFromAccount = strAccount;
652     if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
653         wtx.mapValue["comment"] = params[4].get_str();
654     if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
655         wtx.mapValue["to"]      = params[5].get_str();
656
657     EnsureWalletIsUnlocked();
658
659     // Check funds
660     int64 nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
661     if (nAmount > nBalance)
662         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
663
664     // Send
665     string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
666     if (strError != "")
667         throw JSONRPCError(RPC_WALLET_ERROR, strError);
668
669     return wtx.GetHash().GetHex();
670 }
671
672
673 Value sendmany(const Array& params, bool fHelp)
674 {
675     if (fHelp || params.size() < 2 || params.size() > 4)
676         throw runtime_error(
677             "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
678             "amounts are double-precision floating point numbers"
679             + HelpRequiringPassphrase());
680
681     string strAccount = AccountFromValue(params[0]);
682     Object sendTo = params[1].get_obj();
683     int nMinDepth = 1;
684     if (params.size() > 2)
685         nMinDepth = params[2].get_int();
686
687     CWalletTx wtx;
688     wtx.strFromAccount = strAccount;
689     if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
690         wtx.mapValue["comment"] = params[3].get_str();
691
692     set<CBitcoinAddress> setAddress;
693     vector<pair<CScript, int64> > vecSend;
694
695     int64 totalAmount = 0;
696     BOOST_FOREACH(const Pair& s, sendTo)
697     {
698         CBitcoinAddress address(s.name_);
699         if (!address.IsValid())
700             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
701
702         if (setAddress.count(address))
703             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
704         setAddress.insert(address);
705
706         CScript scriptPubKey;
707         scriptPubKey.SetDestination(address.Get());
708         int64 nAmount = AmountFromValue(s.value_);
709
710         if (nAmount < MIN_TXOUT_AMOUNT)
711             throw JSONRPCError(-101, "Send amount too small");
712
713         totalAmount += nAmount;
714
715         vecSend.push_back(make_pair(scriptPubKey, nAmount));
716     }
717
718     EnsureWalletIsUnlocked();
719
720     // Check funds
721     int64 nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
722     if (totalAmount > nBalance)
723         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
724
725     // Send
726     CReserveKey keyChange(pwalletMain);
727     int64 nFeeRequired = 0;
728     bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
729     if (!fCreated)
730     {
731         int64 nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
732         if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
733             throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
734         throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
735     }
736     if (!pwalletMain->CommitTransaction(wtx, keyChange))
737         throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
738
739     return wtx.GetHash().GetHex();
740 }
741
742 Value addmultisigaddress(const Array& params, bool fHelp)
743 {
744     if (fHelp || params.size() < 2 || params.size() > 3)
745     {
746         string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
747             "Add a nrequired-to-sign multisignature address to the wallet\"\n"
748             "each key is a NovaCoin address or hex-encoded public key\n"
749             "If [account] is specified, assign address to [account].";
750         throw runtime_error(msg);
751     }
752
753     int nRequired = params[0].get_int();
754     const Array& keys = params[1].get_array();
755     string strAccount;
756     if (params.size() > 2)
757         strAccount = AccountFromValue(params[2]);
758
759     // Gather public keys
760     if (nRequired < 1)
761         throw runtime_error("a multisignature address must require at least one key to redeem");
762     if ((int)keys.size() < nRequired)
763         throw runtime_error(
764             strprintf("not enough keys supplied "
765                       "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
766     std::vector<CKey> pubkeys;
767     pubkeys.resize(keys.size());
768     for (unsigned int i = 0; i < keys.size(); i++)
769     {
770         const std::string& ks = keys[i].get_str();
771
772         // Case 1: Bitcoin address and we have full public key:
773         CBitcoinAddress address(ks);
774         if (address.IsValid())
775         {
776             CKeyID keyID;
777             if (!address.GetKeyID(keyID))
778                 throw runtime_error(
779                     strprintf("%s does not refer to a key",ks.c_str()));
780             CPubKey vchPubKey;
781             if (!pwalletMain->GetPubKey(keyID, vchPubKey))
782                 throw runtime_error(
783                     strprintf("no full public key for address %s",ks.c_str()));
784             if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
785                 throw runtime_error(" Invalid public key: "+ks);
786         }
787
788         // Case 2: hex public key
789         else if (IsHex(ks))
790         {
791             CPubKey vchPubKey(ParseHex(ks));
792             if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
793                 throw runtime_error(" Invalid public key: "+ks);
794         }
795         else
796         {
797             throw runtime_error(" Invalid public key: "+ks);
798         }
799     }
800
801     // Construct using pay-to-script-hash:
802     CScript inner;
803     inner.SetMultisig(nRequired, pubkeys);
804     CScriptID innerID = inner.GetID();
805     pwalletMain->AddCScript(inner);
806
807     pwalletMain->SetAddressBookName(innerID, strAccount);
808     return CBitcoinAddress(innerID).ToString();
809 }
810
811 Value addredeemscript(const Array& params, bool fHelp)
812 {
813     if (fHelp || params.size() < 1 || params.size() > 2)
814     {
815         string msg = "addredeemscript <redeemScript> [account]\n"
816             "Add a P2SH address with a specified redeemScript to the wallet.\n"
817             "If [account] is specified, assign address to [account].";
818         throw runtime_error(msg);
819     }
820
821     string strAccount;
822     if (params.size() > 1)
823         strAccount = AccountFromValue(params[1]);
824
825     // Construct using pay-to-script-hash:
826     vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
827     CScript inner(innerData.begin(), innerData.end());
828     CScriptID innerID = inner.GetID();
829     pwalletMain->AddCScript(inner);
830
831     pwalletMain->SetAddressBookName(innerID, strAccount);
832     return CBitcoinAddress(innerID).ToString();
833 }
834
835 struct tallyitem
836 {
837     int64 nAmount;
838     int nConf;
839     tallyitem()
840     {
841         nAmount = 0;
842         nConf = std::numeric_limits<int>::max();
843     }
844 };
845
846 Value ListReceived(const Array& params, bool fByAccounts)
847 {
848     // Minimum confirmations
849     int nMinDepth = 1;
850     if (params.size() > 0)
851         nMinDepth = params[0].get_int();
852
853     // Whether to include empty accounts
854     bool fIncludeEmpty = false;
855     if (params.size() > 1)
856         fIncludeEmpty = params[1].get_bool();
857
858     // Tally
859     map<CBitcoinAddress, tallyitem> mapTally;
860     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
861     {
862         const CWalletTx& wtx = (*it).second;
863
864         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
865             continue;
866
867         int nDepth = wtx.GetDepthInMainChain();
868         if (nDepth < nMinDepth)
869             continue;
870
871         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
872         {
873             CTxDestination address;
874             if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
875                 continue;
876
877             tallyitem& item = mapTally[address];
878             item.nAmount += txout.nValue;
879             item.nConf = min(item.nConf, nDepth);
880         }
881     }
882
883     // Reply
884     Array ret;
885     map<string, tallyitem> mapAccountTally;
886     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
887     {
888         const CBitcoinAddress& address = item.first;
889         const string& strAccount = item.second;
890         map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
891         if (it == mapTally.end() && !fIncludeEmpty)
892             continue;
893
894         int64 nAmount = 0;
895         int nConf = std::numeric_limits<int>::max();
896         if (it != mapTally.end())
897         {
898             nAmount = (*it).second.nAmount;
899             nConf = (*it).second.nConf;
900         }
901
902         if (fByAccounts)
903         {
904             tallyitem& item = mapAccountTally[strAccount];
905             item.nAmount += nAmount;
906             item.nConf = min(item.nConf, nConf);
907         }
908         else
909         {
910             Object obj;
911             obj.push_back(Pair("address",       address.ToString()));
912             obj.push_back(Pair("account",       strAccount));
913             obj.push_back(Pair("amount",        ValueFromAmount(nAmount)));
914             obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
915             ret.push_back(obj);
916         }
917     }
918
919     if (fByAccounts)
920     {
921         for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
922         {
923             int64 nAmount = (*it).second.nAmount;
924             int nConf = (*it).second.nConf;
925             Object obj;
926             obj.push_back(Pair("account",       (*it).first));
927             obj.push_back(Pair("amount",        ValueFromAmount(nAmount)));
928             obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
929             ret.push_back(obj);
930         }
931     }
932
933     return ret;
934 }
935
936 Value listreceivedbyaddress(const Array& params, bool fHelp)
937 {
938     if (fHelp || params.size() > 2)
939         throw runtime_error(
940             "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
941             "[minconf] is the minimum number of confirmations before payments are included.\n"
942             "[includeempty] whether to include addresses that haven't received any payments.\n"
943             "Returns an array of objects containing:\n"
944             "  \"address\" : receiving address\n"
945             "  \"account\" : the account of the receiving address\n"
946             "  \"amount\" : total amount received by the address\n"
947             "  \"confirmations\" : number of confirmations of the most recent transaction included");
948
949     return ListReceived(params, false);
950 }
951
952 Value listreceivedbyaccount(const Array& params, bool fHelp)
953 {
954     if (fHelp || params.size() > 2)
955         throw runtime_error(
956             "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
957             "[minconf] is the minimum number of confirmations before payments are included.\n"
958             "[includeempty] whether to include accounts that haven't received any payments.\n"
959             "Returns an array of objects containing:\n"
960             "  \"account\" : the account of the receiving addresses\n"
961             "  \"amount\" : total amount received by addresses with this account\n"
962             "  \"confirmations\" : number of confirmations of the most recent transaction included");
963
964     return ListReceived(params, true);
965 }
966
967 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
968 {
969     CBitcoinAddress addr;
970     if (addr.Set(dest))
971         entry.push_back(Pair("address", addr.ToString()));
972 }
973
974 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
975 {
976     int64 nGeneratedImmature, nGeneratedMature, nFee;
977     string strSentAccount;
978     list<pair<CTxDestination, int64> > listReceived;
979     list<pair<CTxDestination, int64> > listSent;
980
981     wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
982
983     bool fAllAccounts = (strAccount == string("*"));
984     bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
985
986     // Generated blocks assigned to account ""
987     if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
988     {
989         Object entry;
990         entry.push_back(Pair("account", string("")));
991         if (nGeneratedImmature)
992         {
993             entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
994             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
995         }
996         else
997         {
998             entry.push_back(Pair("category", "generate"));
999             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1000         }
1001         if (fLong)
1002             WalletTxToJSON(wtx, entry);
1003         ret.push_back(entry);
1004     }
1005
1006     // Sent
1007     if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1008     {
1009         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1010         {
1011             Object entry;
1012             entry.push_back(Pair("account", strSentAccount));
1013             if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1014                 entry.push_back(Pair("involvesWatchonly", true));
1015             MaybePushAddress(entry, s.first);
1016
1017             if (wtx.GetDepthInMainChain() < 0) {
1018                 entry.push_back(Pair("category", "conflicted"));
1019             } else {
1020                 entry.push_back(Pair("category", "send"));
1021             }
1022
1023             entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1024             entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1025             if (fLong)
1026                 WalletTxToJSON(wtx, entry);
1027             ret.push_back(entry);
1028         }
1029     }
1030
1031     // Received
1032     if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1033     {
1034         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1035         {
1036             string account;
1037             if (pwalletMain->mapAddressBook.count(r.first))
1038                 account = pwalletMain->mapAddressBook[r.first];
1039             if (fAllAccounts || (account == strAccount))
1040             {
1041                 Object entry;
1042                 entry.push_back(Pair("account", account));
1043                 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1044                     entry.push_back(Pair("involvesWatchonly", true));
1045                 MaybePushAddress(entry, r.first);
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     isminefilter filter = MINE_SPENDABLE;
1101     if(params.size() > 3)
1102         if(params[3].get_bool())
1103             filter = filter | MINE_WATCH_ONLY;
1104
1105     if (nCount < 0)
1106         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1107     if (nFrom < 0)
1108         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1109
1110     Array ret;
1111
1112     std::list<CAccountingEntry> acentries;
1113     CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1114
1115     // iterate backwards until we have nCount items to return:
1116     for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1117     {
1118         CWalletTx *const pwtx = (*it).second.first;
1119         if (pwtx != 0)
1120             ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1121         CAccountingEntry *const pacentry = (*it).second.second;
1122         if (pacentry != 0)
1123             AcentryToJSON(*pacentry, strAccount, ret);
1124
1125         if ((int)ret.size() >= (nCount+nFrom)) break;
1126     }
1127     // ret is newest to oldest
1128
1129     if (nFrom > (int)ret.size())
1130         nFrom = ret.size();
1131     if ((nFrom + nCount) > (int)ret.size())
1132         nCount = ret.size() - nFrom;
1133     Array::iterator first = ret.begin();
1134     std::advance(first, nFrom);
1135     Array::iterator last = ret.begin();
1136     std::advance(last, nFrom+nCount);
1137
1138     if (last != ret.end()) ret.erase(last, ret.end());
1139     if (first != ret.begin()) ret.erase(ret.begin(), first);
1140
1141     std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1142
1143     return ret;
1144 }
1145
1146 Value listaccounts(const Array& params, bool fHelp)
1147 {
1148     if (fHelp || params.size() > 1)
1149         throw runtime_error(
1150             "listaccounts [minconf=1]\n"
1151             "Returns Object that has account names as keys, account balances as values.");
1152
1153     int nMinDepth = 1;
1154     if (params.size() > 0)
1155         nMinDepth = params[0].get_int();
1156
1157     isminefilter includeWatchonly = MINE_SPENDABLE;
1158     if(params.size() > 1)
1159         if(params[1].get_bool())
1160             includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1161
1162
1163     map<string, int64> mapAccountBalances;
1164     BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1165         if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1166             mapAccountBalances[entry.second] = 0;
1167     }
1168
1169     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1170     {
1171         const CWalletTx& wtx = (*it).second;
1172         int64 nGeneratedImmature, nGeneratedMature, nFee;
1173         string strSentAccount;
1174         list<pair<CTxDestination, int64> > listReceived;
1175         list<pair<CTxDestination, int64> > listSent;
1176         wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1177         mapAccountBalances[strSentAccount] -= nFee;
1178         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1179             mapAccountBalances[strSentAccount] -= s.second;
1180         if (wtx.GetDepthInMainChain() >= nMinDepth)
1181         {
1182             mapAccountBalances[""] += nGeneratedMature;
1183             BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1184                 if (pwalletMain->mapAddressBook.count(r.first))
1185                     mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1186                 else
1187                     mapAccountBalances[""] += r.second;
1188         }
1189     }
1190
1191     list<CAccountingEntry> acentries;
1192     CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1193     BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1194         mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1195
1196     Object ret;
1197     BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1198         ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1199     }
1200     return ret;
1201 }
1202
1203 Value listsinceblock(const Array& params, bool fHelp)
1204 {
1205     if (fHelp)
1206         throw runtime_error(
1207             "listsinceblock [blockhash] [target-confirmations]\n"
1208             "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1209
1210     CBlockIndex *pindex = NULL;
1211     int target_confirms = 1;
1212     isminefilter filter = MINE_SPENDABLE;
1213
1214     if (params.size() > 0)
1215     {
1216         uint256 blockId = 0;
1217
1218         blockId.SetHex(params[0].get_str());
1219         pindex = CBlockLocator(blockId).GetBlockIndex();
1220     }
1221
1222     if (params.size() > 1)
1223     {
1224         target_confirms = params[1].get_int();
1225
1226         if (target_confirms < 1)
1227             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1228     }
1229
1230     if(params.size() > 2)
1231         if(params[2].get_bool())
1232             filter = filter | MINE_WATCH_ONLY;
1233
1234     int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1235
1236     Array transactions;
1237
1238     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1239     {
1240         CWalletTx tx = (*it).second;
1241
1242         if (depth == -1 || tx.GetDepthInMainChain() < depth)
1243             ListTransactions(tx, "*", 0, true, transactions, filter);
1244     }
1245
1246     uint256 lastblock;
1247
1248     if (target_confirms == 1)
1249     {
1250         lastblock = hashBestChain;
1251     }
1252     else
1253     {
1254         int target_height = pindexBest->nHeight + 1 - target_confirms;
1255
1256         CBlockIndex *block;
1257         for (block = pindexBest;
1258              block && block->nHeight > target_height;
1259              block = block->pprev)  { }
1260
1261         lastblock = block ? block->GetBlockHash() : 0;
1262     }
1263
1264     Object ret;
1265     ret.push_back(Pair("transactions", transactions));
1266     ret.push_back(Pair("lastblock", lastblock.GetHex()));
1267
1268     return ret;
1269 }
1270
1271 Value gettransaction(const Array& params, bool fHelp)
1272 {
1273     if (fHelp || params.size() != 1)
1274         throw runtime_error(
1275             "gettransaction <txid>\n"
1276             "Get detailed information about <txid>");
1277
1278     uint256 hash;
1279     hash.SetHex(params[0].get_str());
1280
1281     isminefilter filter = MINE_SPENDABLE;
1282     if(params.size() > 1)
1283         if(params[1].get_bool())
1284             filter = filter | MINE_WATCH_ONLY;
1285
1286     Object entry;
1287
1288     if (pwalletMain->mapWallet.count(hash))
1289     {
1290         const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1291
1292         TxToJSON(wtx, 0, entry);
1293
1294         int64 nCredit = wtx.GetCredit(filter);
1295         int64 nDebit = wtx.GetDebit(filter);
1296         int64 nNet = nCredit - nDebit;
1297         int64 nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1298
1299         entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1300         if (wtx.IsFromMe(filter))
1301             entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1302
1303         WalletTxToJSON(wtx, entry);
1304
1305         Array details;
1306         ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1307         entry.push_back(Pair("details", details));
1308     }
1309     else
1310     {
1311         CTransaction tx;
1312         uint256 hashBlock = 0;
1313         if (GetTransaction(hash, tx, hashBlock))
1314         {
1315             TxToJSON(tx, 0, entry);
1316             if (hashBlock == 0)
1317                 entry.push_back(Pair("confirmations", 0));
1318             else
1319             {
1320                 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1321                 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1322                 if (mi != mapBlockIndex.end() && (*mi).second)
1323                 {
1324                     CBlockIndex* pindex = (*mi).second;
1325                     if (pindex->IsInMainChain())
1326                         entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1327                     else
1328                         entry.push_back(Pair("confirmations", 0));
1329                 }
1330             }
1331         }
1332         else
1333             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1334     }
1335
1336     return entry;
1337 }
1338
1339
1340 Value backupwallet(const Array& params, bool fHelp)
1341 {
1342     if (fHelp || params.size() != 1)
1343         throw runtime_error(
1344             "backupwallet <destination>\n"
1345             "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1346
1347     string strDest = params[0].get_str();
1348     if (!BackupWallet(*pwalletMain, strDest))
1349         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1350
1351     return Value::null;
1352 }
1353
1354
1355 Value keypoolrefill(const Array& params, bool fHelp)
1356 {
1357     if (fHelp || params.size() > 1)
1358         throw runtime_error(
1359             "keypoolrefill [new-size]\n"
1360             "Fills the keypool."
1361             + HelpRequiringPassphrase());
1362
1363     unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1364     if (params.size() > 0) {
1365         if (params[0].get_int() < 0)
1366             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1367         nSize = (unsigned int) params[0].get_int();
1368     }
1369
1370     EnsureWalletIsUnlocked();
1371
1372     pwalletMain->TopUpKeyPool(nSize);
1373
1374     if (pwalletMain->GetKeyPoolSize() < nSize)
1375         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1376
1377     return Value::null;
1378 }
1379
1380
1381 void ThreadTopUpKeyPool(void* parg)
1382 {
1383     // Make this thread recognisable as the key-topping-up thread
1384     RenameThread("novacoin-key-top");
1385
1386     pwalletMain->TopUpKeyPool();
1387 }
1388
1389 void ThreadCleanWalletPassphrase(void* parg)
1390 {
1391     // Make this thread recognisable as the wallet relocking thread
1392     RenameThread("novacoin-lock-wa");
1393
1394     int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1395
1396     ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1397
1398     if (nWalletUnlockTime == 0)
1399     {
1400         nWalletUnlockTime = nMyWakeTime;
1401
1402         do
1403         {
1404             if (nWalletUnlockTime==0)
1405                 break;
1406             int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1407             if (nToSleep <= 0)
1408                 break;
1409
1410             LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1411             Sleep(nToSleep);
1412             ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1413
1414         } while(1);
1415
1416         if (nWalletUnlockTime)
1417         {
1418             nWalletUnlockTime = 0;
1419             pwalletMain->Lock();
1420         }
1421     }
1422     else
1423     {
1424         if (nWalletUnlockTime < nMyWakeTime)
1425             nWalletUnlockTime = nMyWakeTime;
1426     }
1427
1428     LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1429
1430     delete (int64*)parg;
1431 }
1432
1433 Value walletpassphrase(const Array& params, bool fHelp)
1434 {
1435     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1436         throw runtime_error(
1437             "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1438             "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1439             "mintonly is optional true/false allowing only block minting.");
1440     if (fHelp)
1441         return true;
1442     if (!pwalletMain->IsCrypted())
1443         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1444
1445     if (!pwalletMain->IsLocked())
1446         throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1447     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1448     SecureString strWalletPass;
1449     strWalletPass.reserve(100);
1450     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1451     // Alternately, find a way to make params[0] mlock()'d to begin with.
1452     strWalletPass = params[0].get_str().c_str();
1453
1454     if (strWalletPass.length() > 0)
1455     {
1456         if (!pwalletMain->Unlock(strWalletPass))
1457             throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1458     }
1459     else
1460         throw runtime_error(
1461             "walletpassphrase <passphrase> <timeout>\n"
1462             "Stores the wallet decryption key in memory for <timeout> seconds.");
1463
1464     NewThread(ThreadTopUpKeyPool, NULL);
1465     int64* pnSleepTime = new int64(params[1].get_int64());
1466     NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1467
1468     // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1469     if (params.size() > 2)
1470         fWalletUnlockMintOnly = params[2].get_bool();
1471     else
1472         fWalletUnlockMintOnly = false;
1473
1474     return Value::null;
1475 }
1476
1477
1478 Value walletpassphrasechange(const Array& params, bool fHelp)
1479 {
1480     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1481         throw runtime_error(
1482             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1483             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1484     if (fHelp)
1485         return true;
1486     if (!pwalletMain->IsCrypted())
1487         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1488
1489     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1490     // Alternately, find a way to make params[0] mlock()'d to begin with.
1491     SecureString strOldWalletPass;
1492     strOldWalletPass.reserve(100);
1493     strOldWalletPass = params[0].get_str().c_str();
1494
1495     SecureString strNewWalletPass;
1496     strNewWalletPass.reserve(100);
1497     strNewWalletPass = params[1].get_str().c_str();
1498
1499     if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1500         throw runtime_error(
1501             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1502             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1503
1504     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1505         throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1506
1507     return Value::null;
1508 }
1509
1510
1511 Value walletlock(const Array& params, bool fHelp)
1512 {
1513     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1514         throw runtime_error(
1515             "walletlock\n"
1516             "Removes the wallet encryption key from memory, locking the wallet.\n"
1517             "After calling this method, you will need to call walletpassphrase again\n"
1518             "before being able to call any methods which require the wallet to be unlocked.");
1519     if (fHelp)
1520         return true;
1521     if (!pwalletMain->IsCrypted())
1522         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1523
1524     {
1525         LOCK(cs_nWalletUnlockTime);
1526         pwalletMain->Lock();
1527         nWalletUnlockTime = 0;
1528     }
1529
1530     return Value::null;
1531 }
1532
1533
1534 Value encryptwallet(const Array& params, bool fHelp)
1535 {
1536     if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1537         throw runtime_error(
1538             "encryptwallet <passphrase>\n"
1539             "Encrypts the wallet with <passphrase>.");
1540     if (fHelp)
1541         return true;
1542     if (pwalletMain->IsCrypted())
1543         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1544
1545     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1546     // Alternately, find a way to make params[0] mlock()'d to begin with.
1547     SecureString strWalletPass;
1548     strWalletPass.reserve(100);
1549     strWalletPass = params[0].get_str().c_str();
1550
1551     if (strWalletPass.length() < 1)
1552         throw runtime_error(
1553             "encryptwallet <passphrase>\n"
1554             "Encrypts the wallet with <passphrase>.");
1555
1556     if (!pwalletMain->EncryptWallet(strWalletPass))
1557         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1558
1559     // BDB seems to have a bad habit of writing old data into
1560     // slack space in .dat files; that is bad if the old data is
1561     // unencrypted private keys. So:
1562     StartShutdown();
1563     return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet.  The keypool has been flushed, you need to make a new backup.";
1564 }
1565
1566 class DescribeAddressVisitor : public boost::static_visitor<Object>
1567 {
1568 private:
1569     isminetype mine;
1570 public:
1571     DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1572
1573     Object operator()(const CNoDestination &dest) const { return Object(); }
1574     Object operator()(const CKeyID &keyID) const {
1575         Object obj;
1576         CPubKey vchPubKey;
1577         pwalletMain->GetPubKey(keyID, vchPubKey);
1578         obj.push_back(Pair("isscript", false));
1579         if (mine == MINE_SPENDABLE) {
1580             pwalletMain->GetPubKey(keyID, vchPubKey);
1581             obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1582             obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1583         }
1584         return obj;
1585     }
1586
1587     Object operator()(const CScriptID &scriptID) const {
1588         Object obj;
1589         obj.push_back(Pair("isscript", true));
1590         if (mine == MINE_SPENDABLE) {
1591             CScript subscript;
1592             pwalletMain->GetCScript(scriptID, subscript);
1593             std::vector<CTxDestination> addresses;
1594             txnouttype whichType;
1595             int nRequired;
1596             ExtractDestinations(subscript, whichType, addresses, nRequired);
1597             obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1598             obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1599             Array a;
1600             BOOST_FOREACH(const CTxDestination& addr, addresses)
1601                 a.push_back(CBitcoinAddress(addr).ToString());
1602             obj.push_back(Pair("addresses", a));
1603             if (whichType == TX_MULTISIG)
1604                 obj.push_back(Pair("sigsrequired", nRequired));
1605         }
1606         return obj;
1607     }
1608 };
1609
1610 Value validateaddress(const Array& params, bool fHelp)
1611 {
1612     if (fHelp || params.size() != 1)
1613         throw runtime_error(
1614             "validateaddress <novacoinaddress>\n"
1615             "Return information about <novacoinaddress>.");
1616
1617     CBitcoinAddress address(params[0].get_str());
1618     bool isValid = address.IsValid();
1619
1620     Object ret;
1621     ret.push_back(Pair("isvalid", isValid));
1622     if (isValid)
1623     {
1624         CTxDestination dest = address.Get();
1625         string currentAddress = address.ToString();
1626         ret.push_back(Pair("address", currentAddress));
1627         isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1628         ret.push_back(Pair("ismine", mine != MINE_NO));
1629         if (mine != MINE_NO) {
1630             ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1631             Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1632             ret.insert(ret.end(), detail.begin(), detail.end());
1633         }
1634         if (pwalletMain->mapAddressBook.count(dest))
1635             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1636     }
1637     return ret;
1638 }
1639
1640 // ppcoin: reserve balance from being staked for network protection
1641 Value reservebalance(const Array& params, bool fHelp)
1642 {
1643     if (fHelp || params.size() > 2)
1644         throw runtime_error(
1645             "reservebalance [<reserve> [amount]]\n"
1646             "<reserve> is true or false to turn balance reserve on or off.\n"
1647             "<amount> is a real and rounded to cent.\n"
1648             "Set reserve amount not participating in network protection.\n"
1649             "If no parameters provided current setting is printed.\n");
1650
1651     if (params.size() > 0)
1652     {
1653         bool fReserve = params[0].get_bool();
1654         if (fReserve)
1655         {
1656             if (params.size() == 1)
1657                 throw runtime_error("must provide amount to reserve balance.\n");
1658             int64 nAmount = AmountFromValue(params[1]);
1659             nAmount = (nAmount / CENT) * CENT;  // round to cent
1660             if (nAmount < 0)
1661                 throw runtime_error("amount cannot be negative.\n");
1662             mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1663         }
1664         else
1665         {
1666             if (params.size() > 1)
1667                 throw runtime_error("cannot specify amount to turn off reserve.\n");
1668             mapArgs["-reservebalance"] = "0";
1669         }
1670     }
1671
1672     Object result;
1673     int64 nReserveBalance = 0;
1674     if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1675         throw runtime_error("invalid reserve balance amount\n");
1676     result.push_back(Pair("reserve", (nReserveBalance > 0)));
1677     result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1678     return result;
1679 }
1680
1681
1682 // ppcoin: check wallet integrity
1683 Value checkwallet(const Array& params, bool fHelp)
1684 {
1685     if (fHelp || params.size() > 0)
1686         throw runtime_error(
1687             "checkwallet\n"
1688             "Check wallet for integrity.\n");
1689
1690     int nMismatchSpent;
1691     int64 nBalanceInQuestion;
1692     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1693     Object result;
1694     if (nMismatchSpent == 0)
1695         result.push_back(Pair("wallet check passed", true));
1696     else
1697     {
1698         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1699         result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1700     }
1701     return result;
1702 }
1703
1704
1705 // ppcoin: repair wallet
1706 Value repairwallet(const Array& params, bool fHelp)
1707 {
1708     if (fHelp || params.size() > 0)
1709         throw runtime_error(
1710             "repairwallet\n"
1711             "Repair wallet if checkwallet reports any problem.\n");
1712
1713     int nMismatchSpent;
1714     int64 nBalanceInQuestion;
1715     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1716     Object result;
1717     if (nMismatchSpent == 0)
1718         result.push_back(Pair("wallet check passed", true));
1719     else
1720     {
1721         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1722         result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1723     }
1724     return result;
1725 }
1726
1727 // NovaCoin: resend unconfirmed wallet transactions
1728 Value resendtx(const Array& params, bool fHelp)
1729 {
1730     if (fHelp || params.size() > 1)
1731         throw runtime_error(
1732             "resendtx\n"
1733             "Re-send unconfirmed transactions.\n"
1734         );
1735
1736     ResendWalletTransactions();
1737
1738     return Value::null;
1739 }
1740
1741 // ppcoin: make a public-private key pair
1742 Value makekeypair(const Array& params, bool fHelp)
1743 {
1744     if (fHelp || params.size() > 1)
1745         throw runtime_error(
1746             "makekeypair [prefix]\n"
1747             "Make a public/private key pair.\n"
1748             "[prefix] is optional preferred prefix for the public key.\n");
1749
1750     string strPrefix = "";
1751     if (params.size() > 0)
1752         strPrefix = params[0].get_str();
1753  
1754     CKey key;
1755     key.MakeNewKey(false);
1756
1757     CPrivKey vchPrivKey = key.GetPrivKey();
1758     Object result;
1759     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1760     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
1761     return result;
1762 }