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