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