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