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