Multi-threaded signatures checking support, rename threads.
[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 " + FormatMoney(MIN_TXOUT_AMOUNT)
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.IsTrusted())
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 " + FormatMoney(MIN_TXOUT_AMOUNT)
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 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
990 {
991     CBitcoinAddress addr;
992     if (addr.Set(dest))
993         entry.push_back(Pair("address", addr.ToString()));
994 }
995
996 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
997 {
998     int64 nGeneratedImmature, nGeneratedMature, nFee;
999     string strSentAccount;
1000     list<pair<CTxDestination, int64> > listReceived;
1001     list<pair<CTxDestination, int64> > listSent;
1002
1003     wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1004
1005     bool fAllAccounts = (strAccount == string("*"));
1006
1007     // Generated blocks assigned to account ""
1008     if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1009     {
1010         Object entry;
1011         entry.push_back(Pair("account", string("")));
1012         if (nGeneratedImmature)
1013         {
1014             entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1015             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1016         }
1017         else
1018         {
1019             entry.push_back(Pair("category", "generate"));
1020             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1021         }
1022         if (fLong)
1023             WalletTxToJSON(wtx, entry);
1024         ret.push_back(entry);
1025     }
1026
1027     // Sent
1028     if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1029     {
1030         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1031         {
1032             Object entry;
1033             entry.push_back(Pair("account", strSentAccount));
1034             MaybePushAddress(entry, s.first);
1035
1036             if (wtx.GetDepthInMainChain() < 0) {
1037                 entry.push_back(Pair("category", "conflicted"));
1038             } else {
1039                 entry.push_back(Pair("category", "send"));
1040             }
1041
1042             entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1043             entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1044             if (fLong)
1045                 WalletTxToJSON(wtx, entry);
1046             ret.push_back(entry);
1047         }
1048     }
1049
1050     // Received
1051     if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1052     {
1053         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1054         {
1055             string account;
1056             if (pwalletMain->mapAddressBook.count(r.first))
1057                 account = pwalletMain->mapAddressBook[r.first];
1058             if (fAllAccounts || (account == strAccount))
1059             {
1060                 Object entry;
1061                 entry.push_back(Pair("account", account));
1062                 MaybePushAddress(entry, r.first);
1063                 if (wtx.IsCoinBase())
1064                 {
1065                     if (wtx.GetDepthInMainChain() < 1)
1066                         entry.push_back(Pair("category", "orphan"));
1067                     else if (wtx.GetBlocksToMaturity() > 0)
1068                         entry.push_back(Pair("category", "immature"));
1069                     else
1070                         entry.push_back(Pair("category", "generate"));
1071                 }
1072                 else
1073                     entry.push_back(Pair("category", "receive"));
1074                 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1075                 if (fLong)
1076                     WalletTxToJSON(wtx, entry);
1077                 ret.push_back(entry);
1078             }
1079         }
1080     }
1081 }
1082
1083 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1084 {
1085     bool fAllAccounts = (strAccount == string("*"));
1086
1087     if (fAllAccounts || acentry.strAccount == strAccount)
1088     {
1089         Object entry;
1090         entry.push_back(Pair("account", acentry.strAccount));
1091         entry.push_back(Pair("category", "move"));
1092         entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1093         entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1094         entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1095         entry.push_back(Pair("comment", acentry.strComment));
1096         ret.push_back(entry);
1097     }
1098 }
1099
1100 Value listtransactions(const Array& params, bool fHelp)
1101 {
1102     if (fHelp || params.size() > 3)
1103         throw runtime_error(
1104             "listtransactions [account] [count=10] [from=0]\n"
1105             "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1106
1107     string strAccount = "*";
1108     if (params.size() > 0)
1109         strAccount = params[0].get_str();
1110     int nCount = 10;
1111     if (params.size() > 1)
1112         nCount = params[1].get_int();
1113     int nFrom = 0;
1114     if (params.size() > 2)
1115         nFrom = params[2].get_int();
1116
1117     if (nCount < 0)
1118         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1119     if (nFrom < 0)
1120         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1121
1122     Array ret;
1123
1124     std::list<CAccountingEntry> acentries;
1125     CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1126
1127     // iterate backwards until we have nCount items to return:
1128     for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1129     {
1130         CWalletTx *const pwtx = (*it).second.first;
1131         if (pwtx != 0)
1132             ListTransactions(*pwtx, strAccount, 0, true, ret);
1133         CAccountingEntry *const pacentry = (*it).second.second;
1134         if (pacentry != 0)
1135             AcentryToJSON(*pacentry, strAccount, ret);
1136
1137         if ((int)ret.size() >= (nCount+nFrom)) break;
1138     }
1139     // ret is newest to oldest
1140
1141     if (nFrom > (int)ret.size())
1142         nFrom = ret.size();
1143     if ((nFrom + nCount) > (int)ret.size())
1144         nCount = ret.size() - nFrom;
1145     Array::iterator first = ret.begin();
1146     std::advance(first, nFrom);
1147     Array::iterator last = ret.begin();
1148     std::advance(last, nFrom+nCount);
1149
1150     if (last != ret.end()) ret.erase(last, ret.end());
1151     if (first != ret.begin()) ret.erase(ret.begin(), first);
1152
1153     std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1154
1155     return ret;
1156 }
1157
1158 Value listaccounts(const Array& params, bool fHelp)
1159 {
1160     if (fHelp || params.size() > 1)
1161         throw runtime_error(
1162             "listaccounts [minconf=1]\n"
1163             "Returns Object that has account names as keys, account balances as values.");
1164
1165     int nMinDepth = 1;
1166     if (params.size() > 0)
1167         nMinDepth = params[0].get_int();
1168
1169     map<string, int64> mapAccountBalances;
1170     BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1171         if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1172             mapAccountBalances[entry.second] = 0;
1173     }
1174
1175     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1176     {
1177         const CWalletTx& wtx = (*it).second;
1178         int64 nGeneratedImmature, nGeneratedMature, nFee;
1179         string strSentAccount;
1180         list<pair<CTxDestination, int64> > listReceived;
1181         list<pair<CTxDestination, int64> > listSent;
1182         wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1183         mapAccountBalances[strSentAccount] -= nFee;
1184         BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1185             mapAccountBalances[strSentAccount] -= s.second;
1186         if (wtx.GetDepthInMainChain() >= nMinDepth)
1187         {
1188             mapAccountBalances[""] += nGeneratedMature;
1189             BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1190                 if (pwalletMain->mapAddressBook.count(r.first))
1191                     mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1192                 else
1193                     mapAccountBalances[""] += r.second;
1194         }
1195     }
1196
1197     list<CAccountingEntry> acentries;
1198     CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1199     BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1200         mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1201
1202     Object ret;
1203     BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1204         ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1205     }
1206     return ret;
1207 }
1208
1209 Value listsinceblock(const Array& params, bool fHelp)
1210 {
1211     if (fHelp)
1212         throw runtime_error(
1213             "listsinceblock [blockhash] [target-confirmations]\n"
1214             "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1215
1216     CBlockIndex *pindex = NULL;
1217     int target_confirms = 1;
1218
1219     if (params.size() > 0)
1220     {
1221         uint256 blockId = 0;
1222
1223         blockId.SetHex(params[0].get_str());
1224         pindex = CBlockLocator(blockId).GetBlockIndex();
1225     }
1226
1227     if (params.size() > 1)
1228     {
1229         target_confirms = params[1].get_int();
1230
1231         if (target_confirms < 1)
1232             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1233     }
1234
1235     int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1236
1237     Array transactions;
1238
1239     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1240     {
1241         CWalletTx tx = (*it).second;
1242
1243         if (depth == -1 || tx.GetDepthInMainChain() < depth)
1244             ListTransactions(tx, "*", 0, true, transactions);
1245     }
1246
1247     uint256 lastblock;
1248
1249     if (target_confirms == 1)
1250     {
1251         lastblock = hashBestChain;
1252     }
1253     else
1254     {
1255         int target_height = pindexBest->nHeight + 1 - target_confirms;
1256
1257         CBlockIndex *block;
1258         for (block = pindexBest;
1259              block && block->nHeight > target_height;
1260              block = block->pprev)  { }
1261
1262         lastblock = block ? block->GetBlockHash() : 0;
1263     }
1264
1265     Object ret;
1266     ret.push_back(Pair("transactions", transactions));
1267     ret.push_back(Pair("lastblock", lastblock.GetHex()));
1268
1269     return ret;
1270 }
1271
1272 Value gettransaction(const Array& params, bool fHelp)
1273 {
1274     if (fHelp || params.size() != 1)
1275         throw runtime_error(
1276             "gettransaction <txid>\n"
1277             "Get detailed information about <txid>");
1278
1279     uint256 hash;
1280     hash.SetHex(params[0].get_str());
1281
1282     Object entry;
1283
1284     if (pwalletMain->mapWallet.count(hash))
1285     {
1286         const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1287
1288         TxToJSON(wtx, 0, entry);
1289
1290         int64 nCredit = wtx.GetCredit();
1291         int64 nDebit = wtx.GetDebit();
1292         int64 nNet = nCredit - nDebit;
1293         int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1294
1295         entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1296         if (wtx.IsFromMe())
1297             entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1298
1299         WalletTxToJSON(wtx, entry);
1300
1301         Array details;
1302         ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1303         entry.push_back(Pair("details", details));
1304     }
1305     else
1306     {
1307         CTransaction tx;
1308         uint256 hashBlock = 0;
1309         if (GetTransaction(hash, tx, hashBlock))
1310         {
1311             TxToJSON(tx, 0, entry);
1312             if (hashBlock == 0)
1313                 entry.push_back(Pair("confirmations", 0));
1314             else
1315             {
1316                 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1317                 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1318                 if (mi != mapBlockIndex.end() && (*mi).second)
1319                 {
1320                     CBlockIndex* pindex = (*mi).second;
1321                     if (pindex->IsInMainChain())
1322                         entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1323                     else
1324                         entry.push_back(Pair("confirmations", 0));
1325                 }
1326             }
1327         }
1328         else
1329             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1330     }
1331
1332     return entry;
1333 }
1334
1335
1336 Value backupwallet(const Array& params, bool fHelp)
1337 {
1338     if (fHelp || params.size() != 1)
1339         throw runtime_error(
1340             "backupwallet <destination>\n"
1341             "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1342
1343     string strDest = params[0].get_str();
1344     if (!BackupWallet(*pwalletMain, strDest))
1345         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1346
1347     return Value::null;
1348 }
1349
1350
1351 Value keypoolrefill(const Array& params, bool fHelp)
1352 {
1353     if (fHelp || params.size() > 1)
1354         throw runtime_error(
1355             "keypoolrefill [new-size]\n"
1356             "Fills the keypool."
1357             + HelpRequiringPassphrase());
1358
1359     unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1360     if (params.size() > 0) {
1361         if (params[0].get_int() < 0)
1362             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1363         nSize = (unsigned int) params[0].get_int();
1364     }
1365
1366     EnsureWalletIsUnlocked();
1367
1368     pwalletMain->TopUpKeyPool(nSize);
1369
1370     if (pwalletMain->GetKeyPoolSize() < nSize)
1371         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1372
1373     return Value::null;
1374 }
1375
1376
1377 void ThreadTopUpKeyPool(void* parg)
1378 {
1379     // Make this thread recognisable as the key-topping-up thread
1380     RenameThread("novacoin-key-top");
1381
1382     pwalletMain->TopUpKeyPool();
1383 }
1384
1385 void ThreadCleanWalletPassphrase(void* parg)
1386 {
1387     // Make this thread recognisable as the wallet relocking thread
1388     RenameThread("novacoin-lock-wa");
1389
1390     int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1391
1392     ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1393
1394     if (nWalletUnlockTime == 0)
1395     {
1396         nWalletUnlockTime = nMyWakeTime;
1397
1398         do
1399         {
1400             if (nWalletUnlockTime==0)
1401                 break;
1402             int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1403             if (nToSleep <= 0)
1404                 break;
1405
1406             LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1407             Sleep(nToSleep);
1408             ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1409
1410         } while(1);
1411
1412         if (nWalletUnlockTime)
1413         {
1414             nWalletUnlockTime = 0;
1415             pwalletMain->Lock();
1416         }
1417     }
1418     else
1419     {
1420         if (nWalletUnlockTime < nMyWakeTime)
1421             nWalletUnlockTime = nMyWakeTime;
1422     }
1423
1424     LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1425
1426     delete (int64*)parg;
1427 }
1428
1429 Value walletpassphrase(const Array& params, bool fHelp)
1430 {
1431     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1432         throw runtime_error(
1433             "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1434             "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1435             "mintonly is optional true/false allowing only block minting.");
1436     if (fHelp)
1437         return true;
1438     if (!pwalletMain->IsCrypted())
1439         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1440
1441     if (!pwalletMain->IsLocked())
1442         throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1443     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1444     SecureString strWalletPass;
1445     strWalletPass.reserve(100);
1446     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1447     // Alternately, find a way to make params[0] mlock()'d to begin with.
1448     strWalletPass = params[0].get_str().c_str();
1449
1450     if (strWalletPass.length() > 0)
1451     {
1452         if (!pwalletMain->Unlock(strWalletPass))
1453             throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1454     }
1455     else
1456         throw runtime_error(
1457             "walletpassphrase <passphrase> <timeout>\n"
1458             "Stores the wallet decryption key in memory for <timeout> seconds.");
1459
1460     NewThread(ThreadTopUpKeyPool, NULL);
1461     int64* pnSleepTime = new int64(params[1].get_int64());
1462     NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1463
1464     // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1465     if (params.size() > 2)
1466         fWalletUnlockMintOnly = params[2].get_bool();
1467     else
1468         fWalletUnlockMintOnly = false;
1469
1470     return Value::null;
1471 }
1472
1473
1474 Value walletpassphrasechange(const Array& params, bool fHelp)
1475 {
1476     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1477         throw runtime_error(
1478             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1479             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1480     if (fHelp)
1481         return true;
1482     if (!pwalletMain->IsCrypted())
1483         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1484
1485     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1486     // Alternately, find a way to make params[0] mlock()'d to begin with.
1487     SecureString strOldWalletPass;
1488     strOldWalletPass.reserve(100);
1489     strOldWalletPass = params[0].get_str().c_str();
1490
1491     SecureString strNewWalletPass;
1492     strNewWalletPass.reserve(100);
1493     strNewWalletPass = params[1].get_str().c_str();
1494
1495     if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1496         throw runtime_error(
1497             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1498             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1499
1500     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1501         throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1502
1503     return Value::null;
1504 }
1505
1506
1507 Value walletlock(const Array& params, bool fHelp)
1508 {
1509     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1510         throw runtime_error(
1511             "walletlock\n"
1512             "Removes the wallet encryption key from memory, locking the wallet.\n"
1513             "After calling this method, you will need to call walletpassphrase again\n"
1514             "before being able to call any methods which require the wallet to be unlocked.");
1515     if (fHelp)
1516         return true;
1517     if (!pwalletMain->IsCrypted())
1518         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1519
1520     {
1521         LOCK(cs_nWalletUnlockTime);
1522         pwalletMain->Lock();
1523         nWalletUnlockTime = 0;
1524     }
1525
1526     return Value::null;
1527 }
1528
1529
1530 Value encryptwallet(const Array& params, bool fHelp)
1531 {
1532     if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1533         throw runtime_error(
1534             "encryptwallet <passphrase>\n"
1535             "Encrypts the wallet with <passphrase>.");
1536     if (fHelp)
1537         return true;
1538     if (pwalletMain->IsCrypted())
1539         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1540
1541     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1542     // Alternately, find a way to make params[0] mlock()'d to begin with.
1543     SecureString strWalletPass;
1544     strWalletPass.reserve(100);
1545     strWalletPass = params[0].get_str().c_str();
1546
1547     if (strWalletPass.length() < 1)
1548         throw runtime_error(
1549             "encryptwallet <passphrase>\n"
1550             "Encrypts the wallet with <passphrase>.");
1551
1552     if (!pwalletMain->EncryptWallet(strWalletPass))
1553         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1554
1555     // BDB seems to have a bad habit of writing old data into
1556     // slack space in .dat files; that is bad if the old data is
1557     // unencrypted private keys. So:
1558     StartShutdown();
1559     return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet.  The keypool has been flushed, you need to make a new backup.";
1560 }
1561
1562 class DescribeAddressVisitor : public boost::static_visitor<Object>
1563 {
1564 public:
1565     Object operator()(const CNoDestination &dest) const { return Object(); }
1566
1567     Object operator()(const CKeyID &keyID) const {
1568         Object obj;
1569         CPubKey vchPubKey;
1570         pwalletMain->GetPubKey(keyID, vchPubKey);
1571         obj.push_back(Pair("isscript", false));
1572         obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1573         obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1574         return obj;
1575     }
1576
1577     Object operator()(const CScriptID &scriptID) const {
1578         Object obj;
1579         obj.push_back(Pair("isscript", true));
1580         CScript subscript;
1581         pwalletMain->GetCScript(scriptID, subscript);
1582         std::vector<CTxDestination> addresses;
1583         txnouttype whichType;
1584         int nRequired;
1585         ExtractDestinations(subscript, whichType, addresses, nRequired);
1586         obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1587         obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1588         Array a;
1589         BOOST_FOREACH(const CTxDestination& addr, addresses)
1590             a.push_back(CBitcoinAddress(addr).ToString());
1591         obj.push_back(Pair("addresses", a));
1592         if (whichType == TX_MULTISIG)
1593             obj.push_back(Pair("sigsrequired", nRequired));
1594         return obj;
1595     }
1596 };
1597
1598 Value validateaddress(const Array& params, bool fHelp)
1599 {
1600     if (fHelp || params.size() != 1)
1601         throw runtime_error(
1602             "validateaddress <novacoinaddress>\n"
1603             "Return information about <novacoinaddress>.");
1604
1605     CBitcoinAddress address(params[0].get_str());
1606     bool isValid = address.IsValid();
1607
1608     Object ret;
1609     ret.push_back(Pair("isvalid", isValid));
1610     if (isValid)
1611     {
1612         CTxDestination dest = address.Get();
1613         string currentAddress = address.ToString();
1614         ret.push_back(Pair("address", currentAddress));
1615         bool fMine = IsMine(*pwalletMain, dest);
1616         ret.push_back(Pair("ismine", fMine));
1617         if (fMine) {
1618             Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1619             ret.insert(ret.end(), detail.begin(), detail.end());
1620         }
1621         if (pwalletMain->mapAddressBook.count(dest))
1622             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1623     }
1624     return ret;
1625 }
1626
1627 Value validatepubkey(const Array& params, bool fHelp)
1628 {
1629     if (fHelp || !params.size() || params.size() > 2)
1630         throw runtime_error(
1631             "validatepubkey <novacoinpubkey>\n"
1632             "Return information about <novacoinpubkey>.");
1633
1634     std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1635     CPubKey pubKey(vchPubKey);
1636
1637     bool isValid = pubKey.IsValid();
1638     bool isCompressed = pubKey.IsCompressed();
1639     CKeyID keyID = pubKey.GetID();
1640
1641     CBitcoinAddress address;
1642     address.Set(keyID);
1643
1644     Object ret;
1645     ret.push_back(Pair("isvalid", isValid));
1646     if (isValid)
1647     {
1648         CTxDestination dest = address.Get();
1649         string currentAddress = address.ToString();
1650         ret.push_back(Pair("address", currentAddress));
1651         bool fMine = IsMine(*pwalletMain, dest);
1652         ret.push_back(Pair("ismine", fMine));
1653         ret.push_back(Pair("iscompressed", isCompressed));
1654         if (fMine) {
1655             Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1656             ret.insert(ret.end(), detail.begin(), detail.end());
1657         }
1658         if (pwalletMain->mapAddressBook.count(dest))
1659             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1660     }
1661     return ret;
1662 }
1663
1664 // ppcoin: reserve balance from being staked for network protection
1665 Value reservebalance(const Array& params, bool fHelp)
1666 {
1667     if (fHelp || params.size() > 2)
1668         throw runtime_error(
1669             "reservebalance [<reserve> [amount]]\n"
1670             "<reserve> is true or false to turn balance reserve on or off.\n"
1671             "<amount> is a real and rounded to cent.\n"
1672             "Set reserve amount not participating in network protection.\n"
1673             "If no parameters provided current setting is printed.\n");
1674
1675     if (params.size() > 0)
1676     {
1677         bool fReserve = params[0].get_bool();
1678         if (fReserve)
1679         {
1680             if (params.size() == 1)
1681                 throw runtime_error("must provide amount to reserve balance.\n");
1682             int64 nAmount = AmountFromValue(params[1]);
1683             nAmount = (nAmount / CENT) * CENT;  // round to cent
1684             if (nAmount < 0)
1685                 throw runtime_error("amount cannot be negative.\n");
1686             mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1687         }
1688         else
1689         {
1690             if (params.size() > 1)
1691                 throw runtime_error("cannot specify amount to turn off reserve.\n");
1692             mapArgs["-reservebalance"] = "0";
1693         }
1694     }
1695
1696     Object result;
1697     int64 nReserveBalance = 0;
1698     if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1699         throw runtime_error("invalid reserve balance amount\n");
1700     result.push_back(Pair("reserve", (nReserveBalance > 0)));
1701     result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1702     return result;
1703 }
1704
1705
1706 // ppcoin: check wallet integrity
1707 Value checkwallet(const Array& params, bool fHelp)
1708 {
1709     if (fHelp || params.size() > 0)
1710         throw runtime_error(
1711             "checkwallet\n"
1712             "Check wallet for integrity.\n");
1713
1714     int nMismatchSpent;
1715     int64 nBalanceInQuestion;
1716     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1717     Object result;
1718     if (nMismatchSpent == 0)
1719         result.push_back(Pair("wallet check passed", true));
1720     else
1721     {
1722         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1723         result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1724     }
1725     return result;
1726 }
1727
1728
1729 // ppcoin: repair wallet
1730 Value repairwallet(const Array& params, bool fHelp)
1731 {
1732     if (fHelp || params.size() > 0)
1733         throw runtime_error(
1734             "repairwallet\n"
1735             "Repair wallet if checkwallet reports any problem.\n");
1736
1737     int nMismatchSpent;
1738     int64 nBalanceInQuestion;
1739     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1740     Object result;
1741     if (nMismatchSpent == 0)
1742         result.push_back(Pair("wallet check passed", true));
1743     else
1744     {
1745         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1746         result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1747     }
1748     return result;
1749 }
1750
1751 // NovaCoin: resend unconfirmed wallet transactions
1752 Value resendtx(const Array& params, bool fHelp)
1753 {
1754     if (fHelp || params.size() > 1)
1755         throw runtime_error(
1756             "resendtx\n"
1757             "Re-send unconfirmed transactions.\n"
1758         );
1759
1760     ResendWalletTransactions();
1761
1762     return Value::null;
1763 }
1764
1765 // ppcoin: make a public-private key pair
1766 Value makekeypair(const Array& params, bool fHelp)
1767 {
1768     if (fHelp || params.size() > 1)
1769         throw runtime_error(
1770             "makekeypair [prefix]\n"
1771             "Make a public/private key pair.\n"
1772             "[prefix] is optional preferred prefix for the public key.\n");
1773
1774     string strPrefix = "";
1775     if (params.size() > 0)
1776         strPrefix = params[0].get_str();
1777  
1778     CKey key;
1779     key.MakeNewKey(false);
1780
1781     CPrivKey vchPrivKey = key.GetPrivKey();
1782     Object result;
1783     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1784     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
1785     return result;
1786 }