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