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