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