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