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