предупреждение о максимальном количестве адресов
[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         ? "\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(MIN_TXOUT_AMOUNT)
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 < MIN_TXOUT_AMOUNT)
282         throw JSONRPCError(-101, "Send amount too small");
283
284     if (nMinValue < MIN_TXOUT_AMOUNT)
285         throw JSONRPCError(-101, "Max value too small");
286
287     if (nOutputValue < MIN_TXOUT_AMOUNT)
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(MIN_TXOUT_AMOUNT)
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 < MIN_TXOUT_AMOUNT)
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 < MIN_TXOUT_AMOUNT)
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(MIN_TXOUT_AMOUNT)
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 < MIN_TXOUT_AMOUNT)
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 < MIN_TXOUT_AMOUNT)
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."
1414             + HelpRequiringPassphrase());
1415
1416     unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
1417     if (params.size() > 0) {
1418         if (params[0].get_int() < 0)
1419             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1420         nSize = (unsigned int) params[0].get_int();
1421     }
1422
1423     EnsureWalletIsUnlocked();
1424
1425     pwalletMain->TopUpKeyPool(nSize);
1426
1427     if (pwalletMain->GetKeyPoolSize() < nSize)
1428         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1429
1430     return Value::null;
1431 }
1432
1433
1434 void ThreadTopUpKeyPool(void* parg)
1435 {
1436     // Make this thread recognisable as the key-topping-up thread
1437     RenameThread("novacoin-key-top");
1438
1439     pwalletMain->TopUpKeyPool();
1440 }
1441
1442 void ThreadCleanWalletPassphrase(void* parg)
1443 {
1444     // Make this thread recognisable as the wallet relocking thread
1445     RenameThread("novacoin-lock-wa");
1446
1447     int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1448
1449     ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1450
1451     if (nWalletUnlockTime == 0)
1452     {
1453         nWalletUnlockTime = nMyWakeTime;
1454
1455         do
1456         {
1457             if (nWalletUnlockTime==0)
1458                 break;
1459             int64_t nToSleep = nWalletUnlockTime - GetTimeMillis();
1460             if (nToSleep <= 0)
1461                 break;
1462
1463             LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1464             Sleep(nToSleep);
1465             ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1466
1467         } while(1);
1468
1469         if (nWalletUnlockTime)
1470         {
1471             nWalletUnlockTime = 0;
1472             pwalletMain->Lock();
1473         }
1474     }
1475     else
1476     {
1477         if (nWalletUnlockTime < nMyWakeTime)
1478             nWalletUnlockTime = nMyWakeTime;
1479     }
1480
1481     LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1482
1483     delete (int64_t*)parg;
1484 }
1485
1486 Value walletpassphrase(const Array& params, bool fHelp)
1487 {
1488     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1489         throw runtime_error(
1490             "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1491             "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1492             "mintonly is optional true/false allowing only block minting.");
1493     if (fHelp)
1494         return true;
1495     if (!pwalletMain->IsCrypted())
1496         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1497
1498     if (!pwalletMain->IsLocked())
1499         throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1500     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1501     SecureString strWalletPass;
1502     strWalletPass.reserve(100);
1503     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1504     // Alternately, find a way to make params[0] mlock()'d to begin with.
1505     strWalletPass = params[0].get_str().c_str();
1506
1507     if (strWalletPass.length() > 0)
1508     {
1509         if (!pwalletMain->Unlock(strWalletPass))
1510             throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1511     }
1512     else
1513         throw runtime_error(
1514             "walletpassphrase <passphrase> <timeout>\n"
1515             "Stores the wallet decryption key in memory for <timeout> seconds.");
1516
1517     NewThread(ThreadTopUpKeyPool, NULL);
1518     int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1519     NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1520
1521     // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1522     if (params.size() > 2)
1523         fWalletUnlockMintOnly = params[2].get_bool();
1524     else
1525         fWalletUnlockMintOnly = false;
1526
1527     return Value::null;
1528 }
1529
1530
1531 Value walletpassphrasechange(const Array& params, bool fHelp)
1532 {
1533     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1534         throw runtime_error(
1535             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1536             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1537     if (fHelp)
1538         return true;
1539     if (!pwalletMain->IsCrypted())
1540         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1541
1542     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1543     // Alternately, find a way to make params[0] mlock()'d to begin with.
1544     SecureString strOldWalletPass;
1545     strOldWalletPass.reserve(100);
1546     strOldWalletPass = params[0].get_str().c_str();
1547
1548     SecureString strNewWalletPass;
1549     strNewWalletPass.reserve(100);
1550     strNewWalletPass = params[1].get_str().c_str();
1551
1552     if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1553         throw runtime_error(
1554             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1555             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1556
1557     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1558         throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1559
1560     return Value::null;
1561 }
1562
1563
1564 Value walletlock(const Array& params, bool fHelp)
1565 {
1566     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1567         throw runtime_error(
1568             "walletlock\n"
1569             "Removes the wallet encryption key from memory, locking the wallet.\n"
1570             "After calling this method, you will need to call walletpassphrase again\n"
1571             "before being able to call any methods which require the wallet to be unlocked.");
1572     if (fHelp)
1573         return true;
1574     if (!pwalletMain->IsCrypted())
1575         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1576
1577     {
1578         LOCK(cs_nWalletUnlockTime);
1579         pwalletMain->Lock();
1580         nWalletUnlockTime = 0;
1581     }
1582
1583     return Value::null;
1584 }
1585
1586
1587 Value encryptwallet(const Array& params, bool fHelp)
1588 {
1589     if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1590         throw runtime_error(
1591             "encryptwallet <passphrase>\n"
1592             "Encrypts the wallet with <passphrase>.");
1593     if (fHelp)
1594         return true;
1595     if (pwalletMain->IsCrypted())
1596         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1597
1598     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1599     // Alternately, find a way to make params[0] mlock()'d to begin with.
1600     SecureString strWalletPass;
1601     strWalletPass.reserve(100);
1602     strWalletPass = params[0].get_str().c_str();
1603
1604     if (strWalletPass.length() < 1)
1605         throw runtime_error(
1606             "encryptwallet <passphrase>\n"
1607             "Encrypts the wallet with <passphrase>.");
1608
1609     if (!pwalletMain->EncryptWallet(strWalletPass))
1610         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1611
1612     // BDB seems to have a bad habit of writing old data into
1613     // slack space in .dat files; that is bad if the old data is
1614     // unencrypted private keys. So:
1615     StartShutdown();
1616     return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet.  The keypool has been flushed, you need to make a new backup.";
1617 }
1618
1619 class DescribeAddressVisitor : public boost::static_visitor<Object>
1620 {
1621 private:
1622     isminetype mine;
1623 public:
1624     DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1625
1626     Object operator()(const CNoDestination &dest) const { return Object(); }
1627     Object operator()(const CKeyID &keyID) const {
1628         Object obj;
1629         CPubKey vchPubKey;
1630         pwalletMain->GetPubKey(keyID, vchPubKey);
1631         obj.push_back(Pair("isscript", false));
1632         if (mine == MINE_SPENDABLE) {
1633             pwalletMain->GetPubKey(keyID, vchPubKey);
1634             obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1635             obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1636         }
1637         return obj;
1638     }
1639
1640     Object operator()(const CScriptID &scriptID) const {
1641         Object obj;
1642         obj.push_back(Pair("isscript", true));
1643         if (mine == MINE_SPENDABLE) {
1644             CScript subscript;
1645             pwalletMain->GetCScript(scriptID, subscript);
1646             std::vector<CTxDestination> addresses;
1647             txnouttype whichType;
1648             int nRequired;
1649             ExtractDestinations(subscript, whichType, addresses, nRequired);
1650             obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1651             obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1652             Array a;
1653             BOOST_FOREACH(const CTxDestination& addr, addresses)
1654                 a.push_back(CBitcoinAddress(addr).ToString());
1655             obj.push_back(Pair("addresses", a));
1656             if (whichType == TX_MULTISIG)
1657                 obj.push_back(Pair("sigsrequired", nRequired));
1658         }
1659         return obj;
1660     }
1661 };
1662
1663 Value validateaddress(const Array& params, bool fHelp)
1664 {
1665     if (fHelp || params.size() != 1)
1666         throw runtime_error(
1667             "validateaddress <novacoinaddress>\n"
1668             "Return information about <novacoinaddress>.");
1669
1670     CBitcoinAddress address(params[0].get_str());
1671     bool isValid = address.IsValid();
1672
1673     Object ret;
1674     ret.push_back(Pair("isvalid", isValid));
1675     if (isValid)
1676     {
1677         CTxDestination dest = address.Get();
1678         string currentAddress = address.ToString();
1679         ret.push_back(Pair("address", currentAddress));
1680         isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1681         ret.push_back(Pair("ismine", mine != MINE_NO));
1682         if (mine != MINE_NO) {
1683             ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1684             Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1685             ret.insert(ret.end(), detail.begin(), detail.end());
1686         }
1687         if (pwalletMain->mapAddressBook.count(dest))
1688             ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1689     }
1690     return ret;
1691 }
1692
1693 // ppcoin: reserve balance from being staked for network protection
1694 Value reservebalance(const Array& params, bool fHelp)
1695 {
1696     if (fHelp || params.size() > 2)
1697         throw runtime_error(
1698             "reservebalance [<reserve> [amount]]\n"
1699             "<reserve> is true or false to turn balance reserve on or off.\n"
1700             "<amount> is a real and rounded to cent.\n"
1701             "Set reserve amount not participating in network protection.\n"
1702             "If no parameters provided current setting is printed.\n");
1703
1704     if (params.size() > 0)
1705     {
1706         bool fReserve = params[0].get_bool();
1707         if (fReserve)
1708         {
1709             if (params.size() == 1)
1710                 throw runtime_error("must provide amount to reserve balance.\n");
1711             int64_t nAmount = AmountFromValue(params[1]);
1712             nAmount = (nAmount / CENT) * CENT;  // round to cent
1713             if (nAmount < 0)
1714                 throw runtime_error("amount cannot be negative.\n");
1715             mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1716         }
1717         else
1718         {
1719             if (params.size() > 1)
1720                 throw runtime_error("cannot specify amount to turn off reserve.\n");
1721             mapArgs["-reservebalance"] = "0";
1722         }
1723     }
1724
1725     Object result;
1726     int64_t nReserveBalance = 0;
1727     if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1728         throw runtime_error("invalid reserve balance amount\n");
1729     result.push_back(Pair("reserve", (nReserveBalance > 0)));
1730     result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1731     return result;
1732 }
1733
1734
1735 // ppcoin: check wallet integrity
1736 Value checkwallet(const Array& params, bool fHelp)
1737 {
1738     if (fHelp || params.size() > 0)
1739         throw runtime_error(
1740             "checkwallet\n"
1741             "Check wallet for integrity.\n");
1742
1743     int nMismatchSpent;
1744     int64_t nBalanceInQuestion;
1745     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1746     Object result;
1747     if (nMismatchSpent == 0)
1748         result.push_back(Pair("wallet check passed", true));
1749     else
1750     {
1751         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1752         result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1753     }
1754     return result;
1755 }
1756
1757
1758 // ppcoin: repair wallet
1759 Value repairwallet(const Array& params, bool fHelp)
1760 {
1761     if (fHelp || params.size() > 0)
1762         throw runtime_error(
1763             "repairwallet\n"
1764             "Repair wallet if checkwallet reports any problem.\n");
1765
1766     int nMismatchSpent;
1767     int64_t nBalanceInQuestion;
1768     pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1769     Object result;
1770     if (nMismatchSpent == 0)
1771         result.push_back(Pair("wallet check passed", true));
1772     else
1773     {
1774         result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1775         result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1776     }
1777     return result;
1778 }
1779
1780 // NovaCoin: resend unconfirmed wallet transactions
1781 Value resendtx(const Array& params, bool fHelp)
1782 {
1783     if (fHelp || params.size() > 1)
1784         throw runtime_error(
1785             "resendtx\n"
1786             "Re-send unconfirmed transactions.\n"
1787         );
1788
1789     ResendWalletTransactions();
1790
1791     return Value::null;
1792 }
1793
1794 // ppcoin: make a public-private key pair
1795 Value makekeypair(const Array& params, bool fHelp)
1796 {
1797     if (fHelp || params.size() > 1)
1798         throw runtime_error(
1799             "makekeypair [prefix]\n"
1800             "Make a public/private key pair.\n"
1801             "[prefix] is optional preferred prefix for the public key.\n");
1802
1803     string strPrefix = "";
1804     if (params.size() > 0)
1805         strPrefix = params[0].get_str();
1806  
1807     CKey key;
1808     key.MakeNewKey(false);
1809
1810     CPrivKey vchPrivKey = key.GetPrivKey();
1811     Object result;
1812     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1813     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
1814     return result;
1815 }