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