]> Git Repo - VerusCoin.git/blame - src/rpcwallet.cpp
Avoid core dump if rpc port is in use.
[VerusCoin.git] / src / rpcwallet.cpp
CommitLineData
e3bc5698
JG
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
fdbb537d
JG
6#include <boost/assign/list_of.hpp>
7
e3bc5698
JG
8#include "wallet.h"
9#include "walletdb.h"
10#include "bitcoinrpc.h"
11#include "init.h"
12#include "base58.h"
13
e3bc5698 14using namespace std;
fdbb537d
JG
15using namespace boost;
16using namespace boost::assign;
17using namespace json_spirit;
e3bc5698
JG
18
19int64 nWalletUnlockTime;
20static CCriticalSection cs_nWalletUnlockTime;
21
bdab0cf5 22std::string HelpRequiringPassphrase()
e3bc5698 23{
b0730874 24 return pwalletMain && pwalletMain->IsCrypted()
e3bc5698
JG
25 ? "\nrequires wallet passphrase to be set with walletpassphrase first"
26 : "";
27}
28
bdab0cf5 29void EnsureWalletIsUnlocked()
e3bc5698
JG
30{
31 if (pwalletMain->IsLocked())
738835d7 32 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
e3bc5698
JG
33}
34
35void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
36{
37 int confirms = wtx.GetDepthInMainChain();
38 entry.push_back(Pair("confirmations", confirms));
e07c8e91
LD
39 if (wtx.IsCoinBase())
40 entry.push_back(Pair("generated", true));
e3bc5698
JG
41 if (confirms)
42 {
43 entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
44 entry.push_back(Pair("blockindex", wtx.nIndex));
bdbfd232 45 entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
e3bc5698
JG
46 }
47 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
48 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
bdbfd232 49 entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
e3bc5698
JG
50 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
51 entry.push_back(Pair(item.first, item.second));
52}
53
54string AccountFromValue(const Value& value)
55{
56 string strAccount = value.get_str();
57 if (strAccount == "*")
738835d7 58 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
e3bc5698
JG
59 return strAccount;
60}
61
c625ae04
JG
62Value 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
81bbef26
PK
69 proxyType proxy;
70 GetProxy(NET_IPV4, proxy);
c625ae04
JG
71
72 Object obj;
73 obj.push_back(Pair("version", (int)CLIENT_VERSION));
74 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
b0730874
JG
75 if (pwalletMain) {
76 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
77 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
78 }
4c6d41b8 79 obj.push_back(Pair("blocks", (int)chainActive.Height()));
8686f646 80 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
c625ae04 81 obj.push_back(Pair("connections", (int)vNodes.size()));
81bbef26 82 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
c625ae04 83 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
0e4b3175 84 obj.push_back(Pair("testnet", TestNet()));
b0730874
JG
85 if (pwalletMain) {
86 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
87 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
88 }
c625ae04 89 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
b0730874 90 if (pwalletMain && pwalletMain->IsCrypted())
92f2c1fe 91 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
c625ae04
JG
92 obj.push_back(Pair("errors", GetWarnings("statusbar")));
93 return obj;
94}
95
96
97
e3bc5698
JG
98Value getnewaddress(const Array& params, bool fHelp)
99{
100 if (fHelp || params.size() > 1)
101 throw runtime_error(
102 "getnewaddress [account]\n"
103 "Returns a new Bitcoin address for receiving payments. "
104 "If [account] is specified (recommended), it is added to the address book "
105 "so payments received with the address will be credited to [account].");
106
107 // Parse the account first so we don't generate a key if there's an error
108 string strAccount;
109 if (params.size() > 0)
110 strAccount = AccountFromValue(params[0]);
111
112 if (!pwalletMain->IsLocked())
113 pwalletMain->TopUpKeyPool();
114
115 // Generate a new key that is added to wallet
116 CPubKey newKey;
71ac5052 117 if (!pwalletMain->GetKeyFromPool(newKey))
738835d7 118 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
e3bc5698
JG
119 CKeyID keyID = newKey.GetID();
120
a41d5fe0 121 pwalletMain->SetAddressBook(keyID, strAccount, "receive");
e3bc5698
JG
122
123 return CBitcoinAddress(keyID).ToString();
124}
125
126
127CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
128{
129 CWalletDB walletdb(pwalletMain->strWalletFile);
130
131 CAccount account;
132 walletdb.ReadAccount(strAccount, account);
133
134 bool bKeyUsed = false;
135
136 // Check if the current key has been used
137 if (account.vchPubKey.IsValid())
138 {
139 CScript scriptPubKey;
140 scriptPubKey.SetDestination(account.vchPubKey.GetID());
141 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
142 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
143 ++it)
144 {
145 const CWalletTx& wtx = (*it).second;
146 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
147 if (txout.scriptPubKey == scriptPubKey)
148 bKeyUsed = true;
149 }
150 }
151
152 // Generate a new key
153 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
154 {
71ac5052 155 if (!pwalletMain->GetKeyFromPool(account.vchPubKey))
738835d7 156 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
e3bc5698 157
a41d5fe0 158 pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
e3bc5698
JG
159 walletdb.WriteAccount(strAccount, account);
160 }
161
162 return CBitcoinAddress(account.vchPubKey.GetID());
163}
164
165Value getaccountaddress(const Array& params, bool fHelp)
166{
167 if (fHelp || params.size() != 1)
168 throw runtime_error(
169 "getaccountaddress <account>\n"
170 "Returns the current Bitcoin address for receiving payments to this account.");
171
172 // Parse the account first so we don't generate a key if there's an error
173 string strAccount = AccountFromValue(params[0]);
174
175 Value ret;
176
177 ret = GetAccountAddress(strAccount).ToString();
178
179 return ret;
180}
181
182
e5e9904c
JG
183Value getrawchangeaddress(const Array& params, bool fHelp)
184{
185 if (fHelp || params.size() > 1)
186 throw runtime_error(
187 "getrawchangeaddress\n"
188 "Returns a new Bitcoin address, for receiving change. "
189 "This is for use with raw transactions, NOT normal use.");
190
191 if (!pwalletMain->IsLocked())
192 pwalletMain->TopUpKeyPool();
193
194 CReserveKey reservekey(pwalletMain);
195 CPubKey vchPubKey;
196 if (!reservekey.GetReservedKey(vchPubKey))
197 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Unable to obtain key for change");
198
199 reservekey.KeepKey();
200
201 CKeyID keyID = vchPubKey.GetID();
202
203 return CBitcoinAddress(keyID).ToString();
204}
205
e3bc5698
JG
206
207Value setaccount(const Array& params, bool fHelp)
208{
209 if (fHelp || params.size() < 1 || params.size() > 2)
210 throw runtime_error(
211 "setaccount <bitcoinaddress> <account>\n"
212 "Sets the account associated with the given address.");
213
214 CBitcoinAddress address(params[0].get_str());
215 if (!address.IsValid())
738835d7 216 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
e3bc5698
JG
217
218
219 string strAccount;
220 if (params.size() > 1)
221 strAccount = AccountFromValue(params[1]);
222
223 // Detect when changing the account of an address that is the 'unused current key' of another account:
224 if (pwalletMain->mapAddressBook.count(address.Get()))
225 {
61885513 226 string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name;
e3bc5698
JG
227 if (address == GetAccountAddress(strOldAccount))
228 GetAccountAddress(strOldAccount, true);
229 }
230
a41d5fe0 231 pwalletMain->SetAddressBook(address.Get(), strAccount, "receive");
e3bc5698
JG
232
233 return Value::null;
234}
235
236
237Value getaccount(const Array& params, bool fHelp)
238{
239 if (fHelp || params.size() != 1)
240 throw runtime_error(
241 "getaccount <bitcoinaddress>\n"
242 "Returns the account associated with the given address.");
243
244 CBitcoinAddress address(params[0].get_str());
245 if (!address.IsValid())
738835d7 246 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
e3bc5698
JG
247
248 string strAccount;
61885513
GA
249 map<CTxDestination, CAddressBookData>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
250 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty())
251 strAccount = (*mi).second.name;
e3bc5698
JG
252 return strAccount;
253}
254
255
256Value getaddressesbyaccount(const Array& params, bool fHelp)
257{
258 if (fHelp || params.size() != 1)
259 throw runtime_error(
260 "getaddressesbyaccount <account>\n"
261 "Returns the list of addresses for the given account.");
262
263 string strAccount = AccountFromValue(params[0]);
264
265 // Find all addresses that have the given account
266 Array ret;
61885513 267 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook)
e3bc5698
JG
268 {
269 const CBitcoinAddress& address = item.first;
61885513 270 const string& strName = item.second.name;
e3bc5698
JG
271 if (strName == strAccount)
272 ret.push_back(address.ToString());
273 }
274 return ret;
275}
276
277Value sendtoaddress(const Array& params, bool fHelp)
278{
279 if (fHelp || params.size() < 2 || params.size() > 4)
280 throw runtime_error(
281 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
282 "<amount> is a real and is rounded to the nearest 0.00000001"
283 + HelpRequiringPassphrase());
284
285 CBitcoinAddress address(params[0].get_str());
286 if (!address.IsValid())
738835d7 287 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
e3bc5698
JG
288
289 // Amount
290 int64 nAmount = AmountFromValue(params[1]);
291
292 // Wallet comments
293 CWalletTx wtx;
294 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
295 wtx.mapValue["comment"] = params[2].get_str();
296 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
297 wtx.mapValue["to"] = params[3].get_str();
298
299 if (pwalletMain->IsLocked())
738835d7 300 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
e3bc5698
JG
301
302 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
303 if (strError != "")
738835d7 304 throw JSONRPCError(RPC_WALLET_ERROR, strError);
e3bc5698
JG
305
306 return wtx.GetHash().GetHex();
307}
308
22dfd735 309Value listaddressgroupings(const Array& params, bool fHelp)
310{
311 if (fHelp)
b1093efa
GM
312 throw runtime_error(
313 "listaddressgroupings\n"
314 "Lists groups of addresses which have had their common ownership\n"
315 "made public by common use as inputs or as the resulting change\n"
316 "in past transactions");
22dfd735 317
318 Array jsonGroupings;
b1093efa
GM
319 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
320 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
22dfd735 321 {
322 Array jsonGrouping;
b1093efa 323 BOOST_FOREACH(CTxDestination address, grouping)
22dfd735 324 {
325 Array addressInfo;
b1093efa 326 addressInfo.push_back(CBitcoinAddress(address).ToString());
22dfd735 327 addressInfo.push_back(ValueFromAmount(balances[address]));
328 {
329 LOCK(pwalletMain->cs_wallet);
330 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
61885513 331 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name);
22dfd735 332 }
333 jsonGrouping.push_back(addressInfo);
334 }
335 jsonGroupings.push_back(jsonGrouping);
336 }
337 return jsonGroupings;
338}
339
e3bc5698
JG
340Value signmessage(const Array& params, bool fHelp)
341{
342 if (fHelp || params.size() != 2)
343 throw runtime_error(
344 "signmessage <bitcoinaddress> <message>\n"
345 "Sign a message with the private key of an address");
346
347 EnsureWalletIsUnlocked();
348
349 string strAddress = params[0].get_str();
350 string strMessage = params[1].get_str();
351
352 CBitcoinAddress addr(strAddress);
353 if (!addr.IsValid())
738835d7 354 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
e3bc5698
JG
355
356 CKeyID keyID;
357 if (!addr.GetKeyID(keyID))
738835d7 358 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
e3bc5698
JG
359
360 CKey key;
361 if (!pwalletMain->GetKey(keyID, key))
738835d7 362 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
e3bc5698 363
8980a509 364 CHashWriter ss(SER_GETHASH, 0);
e3bc5698
JG
365 ss << strMessageMagic;
366 ss << strMessage;
367
368 vector<unsigned char> vchSig;
8980a509 369 if (!key.SignCompact(ss.GetHash(), vchSig))
738835d7 370 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
e3bc5698
JG
371
372 return EncodeBase64(&vchSig[0], vchSig.size());
373}
374
375Value verifymessage(const Array& params, bool fHelp)
376{
377 if (fHelp || params.size() != 3)
378 throw runtime_error(
379 "verifymessage <bitcoinaddress> <signature> <message>\n"
380 "Verify a signed message");
381
382 string strAddress = params[0].get_str();
383 string strSign = params[1].get_str();
384 string strMessage = params[2].get_str();
385
386 CBitcoinAddress addr(strAddress);
387 if (!addr.IsValid())
738835d7 388 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
e3bc5698
JG
389
390 CKeyID keyID;
391 if (!addr.GetKeyID(keyID))
738835d7 392 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
e3bc5698
JG
393
394 bool fInvalid = false;
395 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
396
397 if (fInvalid)
738835d7 398 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
e3bc5698 399
8980a509 400 CHashWriter ss(SER_GETHASH, 0);
e3bc5698
JG
401 ss << strMessageMagic;
402 ss << strMessage;
403
dfa23b94
PW
404 CPubKey pubkey;
405 if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
e3bc5698
JG
406 return false;
407
dfa23b94 408 return (pubkey.GetID() == keyID);
e3bc5698
JG
409}
410
411
412Value getreceivedbyaddress(const Array& params, bool fHelp)
413{
414 if (fHelp || params.size() < 1 || params.size() > 2)
415 throw runtime_error(
416 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
417 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
418
419 // Bitcoin address
420 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
421 CScript scriptPubKey;
422 if (!address.IsValid())
738835d7 423 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
e3bc5698
JG
424 scriptPubKey.SetDestination(address.Get());
425 if (!IsMine(*pwalletMain,scriptPubKey))
426 return (double)0.0;
427
428 // Minimum confirmations
429 int nMinDepth = 1;
430 if (params.size() > 1)
431 nMinDepth = params[1].get_int();
432
433 // Tally
434 int64 nAmount = 0;
435 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
436 {
437 const CWalletTx& wtx = (*it).second;
05df3fc6 438 if (wtx.IsCoinBase() || !IsFinalTx(wtx))
e3bc5698
JG
439 continue;
440
441 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
442 if (txout.scriptPubKey == scriptPubKey)
443 if (wtx.GetDepthInMainChain() >= nMinDepth)
444 nAmount += txout.nValue;
445 }
446
447 return ValueFromAmount(nAmount);
448}
449
450
e3bc5698
JG
451Value getreceivedbyaccount(const Array& params, bool fHelp)
452{
453 if (fHelp || params.size() < 1 || params.size() > 2)
454 throw runtime_error(
455 "getreceivedbyaccount <account> [minconf=1]\n"
456 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
457
458 // Minimum confirmations
459 int nMinDepth = 1;
460 if (params.size() > 1)
461 nMinDepth = params[1].get_int();
462
463 // Get the set of pub keys assigned to account
464 string strAccount = AccountFromValue(params[0]);
3624356e 465 set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount);
e3bc5698
JG
466
467 // Tally
468 int64 nAmount = 0;
469 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
470 {
471 const CWalletTx& wtx = (*it).second;
05df3fc6 472 if (wtx.IsCoinBase() || !IsFinalTx(wtx))
e3bc5698
JG
473 continue;
474
475 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
476 {
477 CTxDestination address;
478 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
479 if (wtx.GetDepthInMainChain() >= nMinDepth)
480 nAmount += txout.nValue;
481 }
482 }
483
484 return (double)nAmount / (double)COIN;
485}
486
487
488int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
489{
490 int64 nBalance = 0;
491
492 // Tally wallet transactions
493 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
494 {
495 const CWalletTx& wtx = (*it).second;
05df3fc6 496 if (!IsFinalTx(wtx))
e3bc5698
JG
497 continue;
498
e07c8e91
LD
499 int64 nReceived, nSent, nFee;
500 wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee);
e3bc5698
JG
501
502 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
503 nBalance += nReceived;
e07c8e91 504 nBalance -= nSent + nFee;
e3bc5698
JG
505 }
506
507 // Tally internal accounting entries
508 nBalance += walletdb.GetAccountCreditDebit(strAccount);
509
510 return nBalance;
511}
512
513int64 GetAccountBalance(const string& strAccount, int nMinDepth)
514{
515 CWalletDB walletdb(pwalletMain->strWalletFile);
516 return GetAccountBalance(walletdb, strAccount, nMinDepth);
517}
518
519
520Value getbalance(const Array& params, bool fHelp)
521{
522 if (fHelp || params.size() > 2)
523 throw runtime_error(
524 "getbalance [account] [minconf=1]\n"
525 "If [account] is not specified, returns the server's total available balance.\n"
526 "If [account] is specified, returns the balance in the account.");
527
528 if (params.size() == 0)
529 return ValueFromAmount(pwalletMain->GetBalance());
530
531 int nMinDepth = 1;
532 if (params.size() > 1)
533 nMinDepth = params[1].get_int();
534
535 if (params[0].get_str() == "*") {
536 // Calculate total balance a different way from GetBalance()
537 // (GetBalance() sums up all unspent TxOuts)
d28bd8b7 538 // getbalance and getbalance '*' 0 should return the same number
e3bc5698
JG
539 int64 nBalance = 0;
540 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
541 {
542 const CWalletTx& wtx = (*it).second;
d28bd8b7 543 if (!wtx.IsConfirmed())
e3bc5698
JG
544 continue;
545
e07c8e91 546 int64 allFee;
e3bc5698
JG
547 string strSentAccount;
548 list<pair<CTxDestination, int64> > listReceived;
549 list<pair<CTxDestination, int64> > listSent;
e07c8e91 550 wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount);
e3bc5698
JG
551 if (wtx.GetDepthInMainChain() >= nMinDepth)
552 {
553 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
554 nBalance += r.second;
555 }
556 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
557 nBalance -= r.second;
558 nBalance -= allFee;
e3bc5698
JG
559 }
560 return ValueFromAmount(nBalance);
561 }
562
563 string strAccount = AccountFromValue(params[0]);
564
565 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
566
567 return ValueFromAmount(nBalance);
568}
569
570
571Value movecmd(const Array& params, bool fHelp)
572{
573 if (fHelp || params.size() < 3 || params.size() > 5)
574 throw runtime_error(
575 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
576 "Move from one account in your wallet to another.");
577
578 string strFrom = AccountFromValue(params[0]);
579 string strTo = AccountFromValue(params[1]);
580 int64 nAmount = AmountFromValue(params[2]);
581 if (params.size() > 3)
582 // unused parameter, used to be nMinDepth, keep type-checking it though
583 (void)params[3].get_int();
584 string strComment;
585 if (params.size() > 4)
586 strComment = params[4].get_str();
587
588 CWalletDB walletdb(pwalletMain->strWalletFile);
589 if (!walletdb.TxnBegin())
738835d7 590 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
e3bc5698
JG
591
592 int64 nNow = GetAdjustedTime();
593
594 // Debit
595 CAccountingEntry debit;
4291e8fe 596 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
e3bc5698
JG
597 debit.strAccount = strFrom;
598 debit.nCreditDebit = -nAmount;
599 debit.nTime = nNow;
600 debit.strOtherAccount = strTo;
601 debit.strComment = strComment;
602 walletdb.WriteAccountingEntry(debit);
603
604 // Credit
605 CAccountingEntry credit;
4291e8fe 606 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
e3bc5698
JG
607 credit.strAccount = strTo;
608 credit.nCreditDebit = nAmount;
609 credit.nTime = nNow;
610 credit.strOtherAccount = strFrom;
611 credit.strComment = strComment;
612 walletdb.WriteAccountingEntry(credit);
613
614 if (!walletdb.TxnCommit())
738835d7 615 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
e3bc5698
JG
616
617 return true;
618}
619
620
621Value sendfrom(const Array& params, bool fHelp)
622{
623 if (fHelp || params.size() < 3 || params.size() > 6)
624 throw runtime_error(
625 "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
626 "<amount> is a real and is rounded to the nearest 0.00000001"
627 + HelpRequiringPassphrase());
628
629 string strAccount = AccountFromValue(params[0]);
630 CBitcoinAddress address(params[1].get_str());
631 if (!address.IsValid())
738835d7 632 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
e3bc5698
JG
633 int64 nAmount = AmountFromValue(params[2]);
634 int nMinDepth = 1;
635 if (params.size() > 3)
636 nMinDepth = params[3].get_int();
637
638 CWalletTx wtx;
639 wtx.strFromAccount = strAccount;
640 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
641 wtx.mapValue["comment"] = params[4].get_str();
642 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
643 wtx.mapValue["to"] = params[5].get_str();
644
645 EnsureWalletIsUnlocked();
646
647 // Check funds
648 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
649 if (nAmount > nBalance)
738835d7 650 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
e3bc5698
JG
651
652 // Send
653 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
654 if (strError != "")
738835d7 655 throw JSONRPCError(RPC_WALLET_ERROR, strError);
e3bc5698
JG
656
657 return wtx.GetHash().GetHex();
658}
659
660
661Value sendmany(const Array& params, bool fHelp)
662{
663 if (fHelp || params.size() < 2 || params.size() > 4)
664 throw runtime_error(
665 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
666 "amounts are double-precision floating point numbers"
667 + HelpRequiringPassphrase());
668
669 string strAccount = AccountFromValue(params[0]);
670 Object sendTo = params[1].get_obj();
671 int nMinDepth = 1;
672 if (params.size() > 2)
673 nMinDepth = params[2].get_int();
674
675 CWalletTx wtx;
676 wtx.strFromAccount = strAccount;
677 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
678 wtx.mapValue["comment"] = params[3].get_str();
679
680 set<CBitcoinAddress> setAddress;
681 vector<pair<CScript, int64> > vecSend;
682
683 int64 totalAmount = 0;
684 BOOST_FOREACH(const Pair& s, sendTo)
685 {
686 CBitcoinAddress address(s.name_);
687 if (!address.IsValid())
738835d7 688 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
e3bc5698
JG
689
690 if (setAddress.count(address))
738835d7 691 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
e3bc5698
JG
692 setAddress.insert(address);
693
694 CScript scriptPubKey;
695 scriptPubKey.SetDestination(address.Get());
696 int64 nAmount = AmountFromValue(s.value_);
697 totalAmount += nAmount;
698
699 vecSend.push_back(make_pair(scriptPubKey, nAmount));
700 }
701
702 EnsureWalletIsUnlocked();
703
704 // Check funds
705 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
706 if (totalAmount > nBalance)
738835d7 707 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
e3bc5698
JG
708
709 // Send
710 CReserveKey keyChange(pwalletMain);
711 int64 nFeeRequired = 0;
1f00f4e9
GA
712 string strFailReason;
713 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason);
e3bc5698 714 if (!fCreated)
1f00f4e9 715 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
e3bc5698 716 if (!pwalletMain->CommitTransaction(wtx, keyChange))
738835d7 717 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
e3bc5698
JG
718
719 return wtx.GetHash().GetHex();
720}
721
34226be7
GA
722//
723// Used by addmultisigaddress / createmultisig:
724//
725static CScript _createmultisig(const Array& params)
e3bc5698 726{
e3bc5698
JG
727 int nRequired = params[0].get_int();
728 const Array& keys = params[1].get_array();
e3bc5698
JG
729
730 // Gather public keys
731 if (nRequired < 1)
732 throw runtime_error("a multisignature address must require at least one key to redeem");
733 if ((int)keys.size() < nRequired)
734 throw runtime_error(
735 strprintf("not enough keys supplied "
d210f4f5 736 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
dfa23b94 737 std::vector<CPubKey> pubkeys;
e3bc5698
JG
738 pubkeys.resize(keys.size());
739 for (unsigned int i = 0; i < keys.size(); i++)
740 {
741 const std::string& ks = keys[i].get_str();
742
743 // Case 1: Bitcoin address and we have full public key:
744 CBitcoinAddress address(ks);
b0730874 745 if (pwalletMain && address.IsValid())
e3bc5698
JG
746 {
747 CKeyID keyID;
748 if (!address.GetKeyID(keyID))
749 throw runtime_error(
750 strprintf("%s does not refer to a key",ks.c_str()));
751 CPubKey vchPubKey;
752 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
753 throw runtime_error(
754 strprintf("no full public key for address %s",ks.c_str()));
dfa23b94 755 if (!vchPubKey.IsFullyValid())
e3bc5698 756 throw runtime_error(" Invalid public key: "+ks);
dfa23b94 757 pubkeys[i] = vchPubKey;
e3bc5698
JG
758 }
759
760 // Case 2: hex public key
761 else if (IsHex(ks))
762 {
763 CPubKey vchPubKey(ParseHex(ks));
dfa23b94 764 if (!vchPubKey.IsFullyValid())
e3bc5698 765 throw runtime_error(" Invalid public key: "+ks);
dfa23b94 766 pubkeys[i] = vchPubKey;
e3bc5698
JG
767 }
768 else
769 {
770 throw runtime_error(" Invalid public key: "+ks);
771 }
772 }
34226be7
GA
773 CScript result;
774 result.SetMultisig(nRequired, pubkeys);
775 return result;
776}
777
778Value addmultisigaddress(const Array& params, bool fHelp)
779{
780 if (fHelp || params.size() < 2 || params.size() > 3)
781 {
782 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
783 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
784 "each key is a Bitcoin address or hex-encoded public key\n"
785 "If [account] is specified, assign address to [account].";
786 throw runtime_error(msg);
787 }
788
789 string strAccount;
790 if (params.size() > 2)
791 strAccount = AccountFromValue(params[2]);
e3bc5698
JG
792
793 // Construct using pay-to-script-hash:
34226be7 794 CScript inner = _createmultisig(params);
e3bc5698
JG
795 CScriptID innerID = inner.GetID();
796 pwalletMain->AddCScript(inner);
797
a41d5fe0 798 pwalletMain->SetAddressBook(innerID, strAccount, "send");
e3bc5698
JG
799 return CBitcoinAddress(innerID).ToString();
800}
801
34226be7
GA
802Value createmultisig(const Array& params, bool fHelp)
803{
804 if (fHelp || params.size() < 2 || params.size() > 2)
805 {
806 string msg = "createmultisig <nrequired> <'[\"key\",\"key\"]'>\n"
807 "Creates a multi-signature address and returns a json object\n"
808 "with keys:\n"
809 "address : bitcoin address\n"
810 "redeemScript : hex-encoded redemption script";
811 throw runtime_error(msg);
812 }
813
814 // Construct using pay-to-script-hash:
815 CScript inner = _createmultisig(params);
816 CScriptID innerID = inner.GetID();
817 CBitcoinAddress address(innerID);
818
819 Object result;
820 result.push_back(Pair("address", address.ToString()));
821 result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
822
823 return result;
824}
825
e3bc5698
JG
826
827struct tallyitem
828{
829 int64 nAmount;
830 int nConf;
62c9b115 831 vector<uint256> txids;
e3bc5698
JG
832 tallyitem()
833 {
834 nAmount = 0;
835 nConf = std::numeric_limits<int>::max();
836 }
837};
838
839Value ListReceived(const Array& params, bool fByAccounts)
840{
841 // Minimum confirmations
842 int nMinDepth = 1;
843 if (params.size() > 0)
844 nMinDepth = params[0].get_int();
845
846 // Whether to include empty accounts
847 bool fIncludeEmpty = false;
848 if (params.size() > 1)
849 fIncludeEmpty = params[1].get_bool();
850
851 // Tally
852 map<CBitcoinAddress, tallyitem> mapTally;
853 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
854 {
855 const CWalletTx& wtx = (*it).second;
856
05df3fc6 857 if (wtx.IsCoinBase() || !IsFinalTx(wtx))
e3bc5698
JG
858 continue;
859
860 int nDepth = wtx.GetDepthInMainChain();
861 if (nDepth < nMinDepth)
862 continue;
863
864 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
865 {
866 CTxDestination address;
867 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
868 continue;
869
870 tallyitem& item = mapTally[address];
871 item.nAmount += txout.nValue;
872 item.nConf = min(item.nConf, nDepth);
62c9b115 873 item.txids.push_back(wtx.GetHash());
e3bc5698
JG
874 }
875 }
876
877 // Reply
878 Array ret;
879 map<string, tallyitem> mapAccountTally;
61885513 880 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook)
e3bc5698
JG
881 {
882 const CBitcoinAddress& address = item.first;
61885513 883 const string& strAccount = item.second.name;
e3bc5698
JG
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)));
62c9b115 909 Array transactions;
1a204694 910 if (it != mapTally.end())
62c9b115 911 {
1a204694
A
912 BOOST_FOREACH(const uint256& item, (*it).second.txids)
913 {
914 transactions.push_back(item.GetHex());
915 }
62c9b115
A
916 }
917 obj.push_back(Pair("txids", transactions));
e3bc5698
JG
918 ret.push_back(obj);
919 }
920 }
921
922 if (fByAccounts)
923 {
924 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
925 {
926 int64 nAmount = (*it).second.nAmount;
927 int nConf = (*it).second.nConf;
928 Object obj;
929 obj.push_back(Pair("account", (*it).first));
930 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
931 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
932 ret.push_back(obj);
933 }
934 }
935
936 return ret;
937}
938
939Value listreceivedbyaddress(const Array& params, bool fHelp)
940{
941 if (fHelp || params.size() > 2)
942 throw runtime_error(
943 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
944 "[minconf] is the minimum number of confirmations before payments are included.\n"
945 "[includeempty] whether to include addresses that haven't received any payments.\n"
946 "Returns an array of objects containing:\n"
947 " \"address\" : receiving address\n"
948 " \"account\" : the account of the receiving address\n"
949 " \"amount\" : total amount received by the address\n"
1a204694
A
950 " \"confirmations\" : number of confirmations of the most recent transaction included\n"
951 " \"txids\" : list of transactions with outputs to the address\n");
e3bc5698
JG
952
953 return ListReceived(params, false);
954}
955
956Value listreceivedbyaccount(const Array& params, bool fHelp)
957{
958 if (fHelp || params.size() > 2)
959 throw runtime_error(
960 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
961 "[minconf] is the minimum number of confirmations before payments are included.\n"
962 "[includeempty] whether to include accounts that haven't received any payments.\n"
963 "Returns an array of objects containing:\n"
964 " \"account\" : the account of the receiving addresses\n"
965 " \"amount\" : total amount received by addresses with this account\n"
966 " \"confirmations\" : number of confirmations of the most recent transaction included");
967
968 return ListReceived(params, true);
969}
970
971void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
972{
e07c8e91 973 int64 nFee;
e3bc5698
JG
974 string strSentAccount;
975 list<pair<CTxDestination, int64> > listReceived;
976 list<pair<CTxDestination, int64> > listSent;
977
e07c8e91 978 wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
e3bc5698
JG
979
980 bool fAllAccounts = (strAccount == string("*"));
981
e3bc5698
JG
982 // Sent
983 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
984 {
985 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
986 {
987 Object entry;
988 entry.push_back(Pair("account", strSentAccount));
989 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
990 entry.push_back(Pair("category", "send"));
991 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
992 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
993 if (fLong)
994 WalletTxToJSON(wtx, entry);
995 ret.push_back(entry);
996 }
997 }
998
999 // Received
1000 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1001 {
1002 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1003 {
1004 string account;
1005 if (pwalletMain->mapAddressBook.count(r.first))
61885513 1006 account = pwalletMain->mapAddressBook[r.first].name;
e3bc5698
JG
1007 if (fAllAccounts || (account == strAccount))
1008 {
1009 Object entry;
1010 entry.push_back(Pair("account", account));
1011 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
e07c8e91
LD
1012 if (wtx.IsCoinBase())
1013 {
1014 if (wtx.GetDepthInMainChain() < 1)
1015 entry.push_back(Pair("category", "orphan"));
1016 else if (wtx.GetBlocksToMaturity() > 0)
1017 entry.push_back(Pair("category", "immature"));
1018 else
1019 entry.push_back(Pair("category", "generate"));
1020 }
1021 else
1022 entry.push_back(Pair("category", "receive"));
e3bc5698
JG
1023 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1024 if (fLong)
1025 WalletTxToJSON(wtx, entry);
1026 ret.push_back(entry);
1027 }
1028 }
1029 }
1030}
1031
1032void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1033{
1034 bool fAllAccounts = (strAccount == string("*"));
1035
1036 if (fAllAccounts || acentry.strAccount == strAccount)
1037 {
1038 Object entry;
1039 entry.push_back(Pair("account", acentry.strAccount));
1040 entry.push_back(Pair("category", "move"));
1041 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1042 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1043 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1044 entry.push_back(Pair("comment", acentry.strComment));
1045 ret.push_back(entry);
1046 }
1047}
1048
1049Value listtransactions(const Array& params, bool fHelp)
1050{
1051 if (fHelp || params.size() > 3)
1052 throw runtime_error(
1053 "listtransactions [account] [count=10] [from=0]\n"
1054 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1055
1056 string strAccount = "*";
1057 if (params.size() > 0)
1058 strAccount = params[0].get_str();
1059 int nCount = 10;
1060 if (params.size() > 1)
1061 nCount = params[1].get_int();
1062 int nFrom = 0;
1063 if (params.size() > 2)
1064 nFrom = params[2].get_int();
1065
1066 if (nCount < 0)
738835d7 1067 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
e3bc5698 1068 if (nFrom < 0)
738835d7 1069 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
e3bc5698
JG
1070
1071 Array ret;
e3bc5698 1072
ddb709e9
LD
1073 std::list<CAccountingEntry> acentries;
1074 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
e3bc5698
JG
1075
1076 // iterate backwards until we have nCount items to return:
c3f95ef1 1077 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
e3bc5698
JG
1078 {
1079 CWalletTx *const pwtx = (*it).second.first;
1080 if (pwtx != 0)
1081 ListTransactions(*pwtx, strAccount, 0, true, ret);
1082 CAccountingEntry *const pacentry = (*it).second.second;
1083 if (pacentry != 0)
1084 AcentryToJSON(*pacentry, strAccount, ret);
1085
1086 if ((int)ret.size() >= (nCount+nFrom)) break;
1087 }
1088 // ret is newest to oldest
1089
1090 if (nFrom > (int)ret.size())
1091 nFrom = ret.size();
1092 if ((nFrom + nCount) > (int)ret.size())
1093 nCount = ret.size() - nFrom;
1094 Array::iterator first = ret.begin();
1095 std::advance(first, nFrom);
1096 Array::iterator last = ret.begin();
1097 std::advance(last, nFrom+nCount);
1098
1099 if (last != ret.end()) ret.erase(last, ret.end());
1100 if (first != ret.begin()) ret.erase(ret.begin(), first);
1101
1102 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1103
1104 return ret;
1105}
1106
1107Value listaccounts(const Array& params, bool fHelp)
1108{
1109 if (fHelp || params.size() > 1)
1110 throw runtime_error(
1111 "listaccounts [minconf=1]\n"
1112 "Returns Object that has account names as keys, account balances as values.");
1113
1114 int nMinDepth = 1;
1115 if (params.size() > 0)
1116 nMinDepth = params[0].get_int();
1117
1118 map<string, int64> mapAccountBalances;
61885513 1119 BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) {
e3bc5698 1120 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
61885513 1121 mapAccountBalances[entry.second.name] = 0;
e3bc5698
JG
1122 }
1123
1124 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1125 {
1126 const CWalletTx& wtx = (*it).second;
e07c8e91 1127 int64 nFee;
e3bc5698
JG
1128 string strSentAccount;
1129 list<pair<CTxDestination, int64> > listReceived;
1130 list<pair<CTxDestination, int64> > listSent;
e07c8e91 1131 wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
e3bc5698
JG
1132 mapAccountBalances[strSentAccount] -= nFee;
1133 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1134 mapAccountBalances[strSentAccount] -= s.second;
1135 if (wtx.GetDepthInMainChain() >= nMinDepth)
1136 {
e3bc5698
JG
1137 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1138 if (pwalletMain->mapAddressBook.count(r.first))
61885513 1139 mapAccountBalances[pwalletMain->mapAddressBook[r.first].name] += r.second;
e3bc5698
JG
1140 else
1141 mapAccountBalances[""] += r.second;
1142 }
1143 }
1144
1145 list<CAccountingEntry> acentries;
1146 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1147 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1148 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1149
1150 Object ret;
1151 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1152 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1153 }
1154 return ret;
1155}
1156
1157Value listsinceblock(const Array& params, bool fHelp)
1158{
1159 if (fHelp)
1160 throw runtime_error(
1161 "listsinceblock [blockhash] [target-confirmations]\n"
9afe5a2c 1162 "Get all wallet transactions in blocks since block [blockhash], or all wallet transactions if omitted");
e3bc5698
JG
1163
1164 CBlockIndex *pindex = NULL;
1165 int target_confirms = 1;
1166
1167 if (params.size() > 0)
1168 {
1169 uint256 blockId = 0;
1170
1171 blockId.SetHex(params[0].get_str());
1172 pindex = CBlockLocator(blockId).GetBlockIndex();
1173 }
1174
1175 if (params.size() > 1)
1176 {
1177 target_confirms = params[1].get_int();
1178
1179 if (target_confirms < 1)
738835d7 1180 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
e3bc5698
JG
1181 }
1182
4c6d41b8 1183 int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
e3bc5698
JG
1184
1185 Array transactions;
1186
1187 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1188 {
1189 CWalletTx tx = (*it).second;
1190
1191 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1192 ListTransactions(tx, "*", 0, true, transactions);
1193 }
1194
4c6d41b8
PW
1195 CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
1196 uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : 0;
e3bc5698
JG
1197
1198 Object ret;
1199 ret.push_back(Pair("transactions", transactions));
1200 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1201
1202 return ret;
1203}
1204
1205Value gettransaction(const Array& params, bool fHelp)
1206{
1207 if (fHelp || params.size() != 1)
1208 throw runtime_error(
1209 "gettransaction <txid>\n"
1210 "Get detailed information about in-wallet transaction <txid>");
1211
1212 uint256 hash;
1213 hash.SetHex(params[0].get_str());
1214
1215 Object entry;
1216 if (!pwalletMain->mapWallet.count(hash))
738835d7 1217 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
e3bc5698
JG
1218 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1219
1220 int64 nCredit = wtx.GetCredit();
1221 int64 nDebit = wtx.GetDebit();
1222 int64 nNet = nCredit - nDebit;
05df3fc6 1223 int64 nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0);
e3bc5698
JG
1224
1225 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1226 if (wtx.IsFromMe())
1227 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1228
1229 WalletTxToJSON(wtx, entry);
1230
1231 Array details;
1232 ListTransactions(wtx, "*", 0, false, details);
1233 entry.push_back(Pair("details", details));
1234
1235 return entry;
1236}
1237
1238
1239Value backupwallet(const Array& params, bool fHelp)
1240{
1241 if (fHelp || params.size() != 1)
1242 throw runtime_error(
1243 "backupwallet <destination>\n"
1244 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1245
1246 string strDest = params[0].get_str();
ad525e9c
PK
1247 if (!BackupWallet(*pwalletMain, strDest))
1248 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
e3bc5698
JG
1249
1250 return Value::null;
1251}
1252
1253
1254Value keypoolrefill(const Array& params, bool fHelp)
1255{
36bd46f1 1256 if (fHelp || params.size() > 1)
e3bc5698 1257 throw runtime_error(
36bd46f1 1258 "keypoolrefill [new-size]\n"
e3bc5698
JG
1259 "Fills the keypool."
1260 + HelpRequiringPassphrase());
1261
36bd46f1
JG
1262 unsigned int kpSize = max(GetArg("-keypool", 100), 0LL);
1263 if (params.size() > 0) {
1264 if (params[0].get_int() < 0)
1265 throw JSONRPCError(-8, "Invalid parameter, expected valid size");
1266 kpSize = (unsigned int) params[0].get_int();
1267 }
1268
e3bc5698
JG
1269 EnsureWalletIsUnlocked();
1270
36bd46f1 1271 pwalletMain->TopUpKeyPool(kpSize);
e3bc5698 1272
36bd46f1 1273 if (pwalletMain->GetKeyPoolSize() < kpSize)
738835d7 1274 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
e3bc5698
JG
1275
1276 return Value::null;
1277}
1278
1279
92f2c1fe 1280static void LockWallet(CWallet* pWallet)
e3bc5698 1281{
92f2c1fe
GA
1282 LOCK(cs_nWalletUnlockTime);
1283 nWalletUnlockTime = 0;
1284 pWallet->Lock();
e3bc5698
JG
1285}
1286
1287Value walletpassphrase(const Array& params, bool fHelp)
1288{
1289 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1290 throw runtime_error(
1291 "walletpassphrase <passphrase> <timeout>\n"
1292 "Stores the wallet decryption key in memory for <timeout> seconds.");
1293 if (fHelp)
1294 return true;
1295 if (!pwalletMain->IsCrypted())
738835d7 1296 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
e3bc5698 1297
e3bc5698
JG
1298 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1299 SecureString strWalletPass;
1300 strWalletPass.reserve(100);
1301 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1302 // Alternately, find a way to make params[0] mlock()'d to begin with.
1303 strWalletPass = params[0].get_str().c_str();
1304
1305 if (strWalletPass.length() > 0)
1306 {
1307 if (!pwalletMain->Unlock(strWalletPass))
738835d7 1308 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
e3bc5698
JG
1309 }
1310 else
1311 throw runtime_error(
1312 "walletpassphrase <passphrase> <timeout>\n"
1313 "Stores the wallet decryption key in memory for <timeout> seconds.");
1314
92f2c1fe
GA
1315 pwalletMain->TopUpKeyPool();
1316
1317 int64 nSleepTime = params[1].get_int64();
1318 LOCK(cs_nWalletUnlockTime);
1319 nWalletUnlockTime = GetTime() + nSleepTime;
1320 RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime);
e3bc5698
JG
1321
1322 return Value::null;
1323}
1324
1325
1326Value walletpassphrasechange(const Array& params, bool fHelp)
1327{
1328 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1329 throw runtime_error(
1330 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1331 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1332 if (fHelp)
1333 return true;
1334 if (!pwalletMain->IsCrypted())
738835d7 1335 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
e3bc5698
JG
1336
1337 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1338 // Alternately, find a way to make params[0] mlock()'d to begin with.
1339 SecureString strOldWalletPass;
1340 strOldWalletPass.reserve(100);
1341 strOldWalletPass = params[0].get_str().c_str();
1342
1343 SecureString strNewWalletPass;
1344 strNewWalletPass.reserve(100);
1345 strNewWalletPass = params[1].get_str().c_str();
1346
1347 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1348 throw runtime_error(
1349 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1350 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1351
1352 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
738835d7 1353 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
e3bc5698
JG
1354
1355 return Value::null;
1356}
1357
1358
1359Value walletlock(const Array& params, bool fHelp)
1360{
1361 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1362 throw runtime_error(
1363 "walletlock\n"
1364 "Removes the wallet encryption key from memory, locking the wallet.\n"
1365 "After calling this method, you will need to call walletpassphrase again\n"
1366 "before being able to call any methods which require the wallet to be unlocked.");
1367 if (fHelp)
1368 return true;
1369 if (!pwalletMain->IsCrypted())
738835d7 1370 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
e3bc5698
JG
1371
1372 {
1373 LOCK(cs_nWalletUnlockTime);
1374 pwalletMain->Lock();
1375 nWalletUnlockTime = 0;
1376 }
1377
1378 return Value::null;
1379}
1380
1381
1382Value encryptwallet(const Array& params, bool fHelp)
1383{
1384 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1385 throw runtime_error(
1386 "encryptwallet <passphrase>\n"
1387 "Encrypts the wallet with <passphrase>.");
1388 if (fHelp)
1389 return true;
1390 if (pwalletMain->IsCrypted())
738835d7 1391 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
e3bc5698
JG
1392
1393 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1394 // Alternately, find a way to make params[0] mlock()'d to begin with.
1395 SecureString strWalletPass;
1396 strWalletPass.reserve(100);
1397 strWalletPass = params[0].get_str().c_str();
1398
1399 if (strWalletPass.length() < 1)
1400 throw runtime_error(
1401 "encryptwallet <passphrase>\n"
1402 "Encrypts the wallet with <passphrase>.");
1403
1404 if (!pwalletMain->EncryptWallet(strWalletPass))
738835d7 1405 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
e3bc5698
JG
1406
1407 // BDB seems to have a bad habit of writing old data into
1408 // slack space in .dat files; that is bad if the old data is
331544bc 1409 // unencrypted private keys. So:
e3bc5698 1410 StartShutdown();
6b3783a9 1411 return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
e3bc5698
JG
1412}
1413
1414class DescribeAddressVisitor : public boost::static_visitor<Object>
1415{
1416public:
1417 Object operator()(const CNoDestination &dest) const { return Object(); }
1418
1419 Object operator()(const CKeyID &keyID) const {
1420 Object obj;
1421 CPubKey vchPubKey;
1422 pwalletMain->GetPubKey(keyID, vchPubKey);
1423 obj.push_back(Pair("isscript", false));
5d891489 1424 obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
e3bc5698
JG
1425 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1426 return obj;
1427 }
1428
1429 Object operator()(const CScriptID &scriptID) const {
1430 Object obj;
1431 obj.push_back(Pair("isscript", true));
1432 CScript subscript;
1433 pwalletMain->GetCScript(scriptID, subscript);
1434 std::vector<CTxDestination> addresses;
1435 txnouttype whichType;
1436 int nRequired;
1437 ExtractDestinations(subscript, whichType, addresses, nRequired);
1438 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
22536422 1439 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
e3bc5698
JG
1440 Array a;
1441 BOOST_FOREACH(const CTxDestination& addr, addresses)
1442 a.push_back(CBitcoinAddress(addr).ToString());
1443 obj.push_back(Pair("addresses", a));
1444 if (whichType == TX_MULTISIG)
1445 obj.push_back(Pair("sigsrequired", nRequired));
1446 return obj;
1447 }
1448};
1449
1450Value validateaddress(const Array& params, bool fHelp)
1451{
1452 if (fHelp || params.size() != 1)
1453 throw runtime_error(
1454 "validateaddress <bitcoinaddress>\n"
1455 "Return information about <bitcoinaddress>.");
1456
1457 CBitcoinAddress address(params[0].get_str());
1458 bool isValid = address.IsValid();
1459
1460 Object ret;
1461 ret.push_back(Pair("isvalid", isValid));
1462 if (isValid)
1463 {
1464 CTxDestination dest = address.Get();
1465 string currentAddress = address.ToString();
1466 ret.push_back(Pair("address", currentAddress));
28f6b8db 1467 bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false;
e3bc5698
JG
1468 ret.push_back(Pair("ismine", fMine));
1469 if (fMine) {
1470 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1471 ret.insert(ret.end(), detail.begin(), detail.end());
1472 }
28f6b8db 1473 if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
61885513 1474 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));
e3bc5698
JG
1475 }
1476 return ret;
1477}
1478
fdbb537d
JG
1479Value lockunspent(const Array& params, bool fHelp)
1480{
1481 if (fHelp || params.size() < 1 || params.size() > 2)
1482 throw runtime_error(
1483 "lockunspent unlock? [array-of-Objects]\n"
1484 "Updates list of temporarily unspendable outputs.");
1485
1486 if (params.size() == 1)
1487 RPCTypeCheck(params, list_of(bool_type));
1488 else
1489 RPCTypeCheck(params, list_of(bool_type)(array_type));
1490
1491 bool fUnlock = params[0].get_bool();
1492
1493 if (params.size() == 1) {
1494 if (fUnlock)
1495 pwalletMain->UnlockAllCoins();
1496 return true;
1497 }
1498
1499 Array outputs = params[1].get_array();
1500 BOOST_FOREACH(Value& output, outputs)
1501 {
1502 if (output.type() != obj_type)
15117692 1503 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
fdbb537d
JG
1504 const Object& o = output.get_obj();
1505
1506 RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type));
1507
1508 string txid = find_value(o, "txid").get_str();
1509 if (!IsHex(txid))
15117692 1510 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
fdbb537d
JG
1511
1512 int nOutput = find_value(o, "vout").get_int();
1513 if (nOutput < 0)
15117692 1514 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
fdbb537d
JG
1515
1516 COutPoint outpt(uint256(txid), nOutput);
1517
1518 if (fUnlock)
1519 pwalletMain->UnlockCoin(outpt);
1520 else
1521 pwalletMain->LockCoin(outpt);
1522 }
1523
1524 return true;
1525}
1526
1527Value listlockunspent(const Array& params, bool fHelp)
1528{
1529 if (fHelp || params.size() > 0)
1530 throw runtime_error(
1531 "listlockunspent\n"
1532 "Returns list of temporarily unspendable outputs.");
1533
1534 vector<COutPoint> vOutpts;
1535 pwalletMain->ListLockedCoins(vOutpts);
1536
1537 Array ret;
1538
1539 BOOST_FOREACH(COutPoint &outpt, vOutpts) {
1540 Object o;
1541
1542 o.push_back(Pair("txid", outpt.hash.GetHex()));
1543 o.push_back(Pair("vout", (int)outpt.n));
1544 ret.push_back(o);
1545 }
1546
1547 return ret;
1548}
1549
This page took 0.236583 seconds and 4 git commands to generate.