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