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