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