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.
8 #include "bitcoinrpc.h"
12 using namespace json_spirit;
15 int64 nWalletUnlockTime;
16 static CCriticalSection cs_nWalletUnlockTime;
18 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, json_spirit::Object& entry);
20 std::string HelpRequiringPassphrase()
22 return pwalletMain->IsCrypted()
23 ? "\nrequires wallet passphrase to be set with walletpassphrase first"
27 void EnsureWalletIsUnlocked()
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.");
35 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
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));
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)));
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));
54 string AccountFromValue(const Value& value)
56 string strAccount = value.get_str();
57 if (strAccount == "*")
58 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
62 Value getinfo(const Array& params, bool fHelp)
64 if (fHelp || params.size() != 0)
67 "Returns an object containing various state info.");
70 GetProxy(NET_IPV4, proxy);
72 int64 nTotal = 0, nWatchOnly = 0;
73 pwalletMain->GetBalance(nTotal, nWatchOnly);
76 obj.push_back(Pair("version", FormatFullVersion()));
77 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
78 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
79 obj.push_back(Pair("balance", ValueFromAmount(nTotal)));
80 obj.push_back(Pair("unspendable", ValueFromAmount(nWatchOnly)));
81 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
82 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
83 obj.push_back(Pair("blocks", (int)nBestHeight));
84 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
85 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
86 obj.push_back(Pair("connections", (int)vNodes.size()));
87 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
88 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
90 diff.push_back(Pair("proof-of-work", GetDifficulty()));
91 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
92 obj.push_back(Pair("difficulty", diff));
94 obj.push_back(Pair("testnet", fTestNet));
95 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
96 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
97 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
98 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
99 if (pwalletMain->IsCrypted())
100 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
101 obj.push_back(Pair("errors", GetWarnings("statusbar")));
105 Value getnewaddress(const Array& params, bool fHelp)
107 if (fHelp || params.size() > 1)
109 "getnewaddress [account]\n"
110 "Returns a new NovaCoin address for receiving payments. "
111 "If [account] is specified (recommended), it is added to the address book "
112 "so payments received with the address will be credited to [account].");
114 // Parse the account first so we don't generate a key if there's an error
116 if (params.size() > 0)
117 strAccount = AccountFromValue(params[0]);
119 if (!pwalletMain->IsLocked())
120 pwalletMain->TopUpKeyPool();
122 // Generate a new key that is added to wallet
124 if (!pwalletMain->GetKeyFromPool(newKey, false))
125 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
126 CKeyID keyID = newKey.GetID();
128 pwalletMain->SetAddressBookName(keyID, strAccount);
130 return CBitcoinAddress(keyID).ToString();
134 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
136 CWalletDB walletdb(pwalletMain->strWalletFile);
139 walletdb.ReadAccount(strAccount, account);
141 bool bKeyUsed = false;
143 // Check if the current key has been used
144 if (account.vchPubKey.IsValid())
146 CScript scriptPubKey;
147 scriptPubKey.SetDestination(account.vchPubKey.GetID());
148 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
149 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
152 const CWalletTx& wtx = (*it).second;
153 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
154 if (txout.scriptPubKey == scriptPubKey)
159 // Generate a new key
160 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
162 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
163 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
165 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
166 walletdb.WriteAccount(strAccount, account);
169 return CBitcoinAddress(account.vchPubKey.GetID());
172 Value getaccountaddress(const Array& params, bool fHelp)
174 if (fHelp || params.size() != 1)
176 "getaccountaddress <account>\n"
177 "Returns the current NovaCoin address for receiving payments to this account.");
179 // Parse the account first so we don't generate a key if there's an error
180 string strAccount = AccountFromValue(params[0]);
184 ret = GetAccountAddress(strAccount).ToString();
191 Value setaccount(const Array& params, bool fHelp)
193 if (fHelp || params.size() < 1 || params.size() > 2)
195 "setaccount <novacoinaddress> <account>\n"
196 "Sets the account associated with the given address.");
198 CBitcoinAddress address(params[0].get_str());
199 if (!address.IsValid())
200 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
204 if (params.size() > 1)
205 strAccount = AccountFromValue(params[1]);
207 // Detect when changing the account of an address that is the 'unused current key' of another account:
208 if (pwalletMain->mapAddressBook.count(address.Get()))
210 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
211 if (address == GetAccountAddress(strOldAccount))
212 GetAccountAddress(strOldAccount, true);
215 pwalletMain->SetAddressBookName(address.Get(), strAccount);
221 Value getaccount(const Array& params, bool fHelp)
223 if (fHelp || params.size() != 1)
225 "getaccount <novacoinaddress>\n"
226 "Returns the account associated with the given address.");
228 CBitcoinAddress address(params[0].get_str());
229 if (!address.IsValid())
230 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
233 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
234 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
235 strAccount = (*mi).second;
240 Value getaddressesbyaccount(const Array& params, bool fHelp)
242 if (fHelp || params.size() != 1)
244 "getaddressesbyaccount <account>\n"
245 "Returns the list of addresses for the given account.");
247 string strAccount = AccountFromValue(params[0]);
249 // Find all addresses that have the given account
251 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
253 const CBitcoinAddress& address = item.first;
254 const string& strName = item.second;
255 if (strName == strAccount)
256 ret.push_back(address.ToString());
261 Value sendtoaddress(const Array& params, bool fHelp)
263 if (fHelp || params.size() < 2 || params.size() > 4)
265 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
266 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
267 + HelpRequiringPassphrase());
269 CBitcoinAddress address(params[0].get_str());
270 if (!address.IsValid())
271 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
274 int64 nAmount = AmountFromValue(params[1]);
276 if (nAmount < MIN_TXOUT_AMOUNT)
277 throw JSONRPCError(-101, "Send amount too small");
281 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
282 wtx.mapValue["comment"] = params[2].get_str();
283 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
284 wtx.mapValue["to"] = params[3].get_str();
286 if (pwalletMain->IsLocked())
287 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
289 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
291 throw JSONRPCError(RPC_WALLET_ERROR, strError);
293 return wtx.GetHash().GetHex();
296 Value listaddressgroupings(const Array& params, bool fHelp)
300 "listaddressgroupings\n"
301 "Lists groups of addresses which have had their common ownership\n"
302 "made public by common use as inputs or as the resulting change\n"
303 "in past transactions");
306 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
307 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
310 BOOST_FOREACH(CTxDestination address, grouping)
313 addressInfo.push_back(CBitcoinAddress(address).ToString());
314 addressInfo.push_back(ValueFromAmount(balances[address]));
316 LOCK(pwalletMain->cs_wallet);
317 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
318 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
320 jsonGrouping.push_back(addressInfo);
322 jsonGroupings.push_back(jsonGrouping);
324 return jsonGroupings;
327 Value signmessage(const Array& params, bool fHelp)
329 if (fHelp || params.size() != 2)
331 "signmessage <novacoinaddress> <message>\n"
332 "Sign a message with the private key of an address");
334 EnsureWalletIsUnlocked();
336 string strAddress = params[0].get_str();
337 string strMessage = params[1].get_str();
339 CBitcoinAddress addr(strAddress);
341 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
344 if (!addr.GetKeyID(keyID))
345 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
348 if (!pwalletMain->GetKey(keyID, key))
349 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
351 CDataStream ss(SER_GETHASH, 0);
352 ss << strMessageMagic;
355 vector<unsigned char> vchSig;
356 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
357 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
359 return EncodeBase64(&vchSig[0], vchSig.size());
362 Value verifymessage(const Array& params, bool fHelp)
364 if (fHelp || params.size() != 3)
366 "verifymessage <novacoinaddress> <signature> <message>\n"
367 "Verify a signed message");
369 string strAddress = params[0].get_str();
370 string strSign = params[1].get_str();
371 string strMessage = params[2].get_str();
373 CBitcoinAddress addr(strAddress);
375 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
378 if (!addr.GetKeyID(keyID))
379 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
381 bool fInvalid = false;
382 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
385 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
387 CDataStream ss(SER_GETHASH, 0);
388 ss << strMessageMagic;
392 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
395 return (key.GetPubKey().GetID() == keyID);
399 Value getreceivedbyaddress(const Array& params, bool fHelp)
401 if (fHelp || params.size() < 1 || params.size() > 2)
403 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
404 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
407 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
408 CScript scriptPubKey;
409 if (!address.IsValid())
410 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
411 scriptPubKey.SetDestination(address.Get());
412 if (!IsMine(*pwalletMain,scriptPubKey))
415 // Minimum confirmations
417 if (params.size() > 1)
418 nMinDepth = params[1].get_int();
422 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
424 const CWalletTx& wtx = (*it).second;
425 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
428 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
429 if (txout.scriptPubKey == scriptPubKey)
430 if (wtx.GetDepthInMainChain() >= nMinDepth)
431 nAmount += txout.nValue;
434 return ValueFromAmount(nAmount);
438 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
440 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
442 const CTxDestination& address = item.first;
443 const string& strName = item.second;
444 if (strName == strAccount)
445 setAddress.insert(address);
449 Value getreceivedbyaccount(const Array& params, bool fHelp)
451 if (fHelp || params.size() < 1 || params.size() > 2)
453 "getreceivedbyaccount <account> [minconf=1]\n"
454 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
456 // Minimum confirmations
458 if (params.size() > 1)
459 nMinDepth = params[1].get_int();
461 // Get the set of pub keys assigned to account
462 string strAccount = AccountFromValue(params[0]);
463 set<CTxDestination> setAddress;
464 GetAccountAddresses(strAccount, setAddress);
468 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
470 const CWalletTx& wtx = (*it).second;
471 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
474 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
476 CTxDestination address;
477 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
478 if (wtx.GetDepthInMainChain() >= nMinDepth)
479 nAmount += txout.nValue;
483 return (double)nAmount / (double)COIN;
487 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
491 // Tally wallet transactions
492 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
494 const CWalletTx& wtx = (*it).second;
498 int64 nGenerated, nReceived, nSent, nFee;
499 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
501 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
502 nBalance += nReceived;
503 nBalance += nGenerated - nSent - nFee;
506 // Tally internal accounting entries
507 nBalance += walletdb.GetAccountCreditDebit(strAccount);
512 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
514 CWalletDB walletdb(pwalletMain->strWalletFile);
515 return GetAccountBalance(walletdb, strAccount, nMinDepth);
519 Value getbalance(const Array& params, bool fHelp)
521 if (fHelp || params.size() > 2)
523 "getbalance [account] [minconf=1]\n"
524 "If [account] is not specified, returns the server's total available balance.\n"
525 "If [account] is specified, returns the balance in the account.");
527 if (params.size() == 0)
528 return ValueFromAmount(pwalletMain->GetBalance());
531 if (params.size() > 1)
532 nMinDepth = params[1].get_int();
534 if (params[0].get_str() == "*") {
535 // Calculate total balance a different way from GetBalance()
536 // (GetBalance() sums up all unspent TxOuts)
537 // getbalance and getbalance '*' 0 should return the same number.
539 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
541 const CWalletTx& wtx = (*it).second;
542 if (!wtx.IsTrusted())
545 int64 allGeneratedImmature, allGeneratedMature, allFee;
546 allGeneratedImmature = allGeneratedMature = allFee = 0;
548 string strSentAccount;
549 list<pair<CTxDestination, int64> > listReceived;
550 list<pair<CTxDestination, int64> > listSent;
551 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
552 if (wtx.GetDepthInMainChain() >= nMinDepth)
554 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
555 nBalance += r.second;
557 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
558 nBalance -= r.second;
560 nBalance += allGeneratedMature;
562 return ValueFromAmount(nBalance);
565 string strAccount = AccountFromValue(params[0]);
567 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
569 return ValueFromAmount(nBalance);
573 Value movecmd(const Array& params, bool fHelp)
575 if (fHelp || params.size() < 3 || params.size() > 5)
577 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
578 "Move from one account in your wallet to another.");
580 string strFrom = AccountFromValue(params[0]);
581 string strTo = AccountFromValue(params[1]);
582 int64 nAmount = AmountFromValue(params[2]);
584 if (nAmount < MIN_TXOUT_AMOUNT)
585 throw JSONRPCError(-101, "Send amount too small");
587 if (params.size() > 3)
588 // unused parameter, used to be nMinDepth, keep type-checking it though
589 (void)params[3].get_int();
591 if (params.size() > 4)
592 strComment = params[4].get_str();
594 CWalletDB walletdb(pwalletMain->strWalletFile);
595 if (!walletdb.TxnBegin())
596 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
598 int64 nNow = GetAdjustedTime();
601 CAccountingEntry debit;
602 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
603 debit.strAccount = strFrom;
604 debit.nCreditDebit = -nAmount;
606 debit.strOtherAccount = strTo;
607 debit.strComment = strComment;
608 walletdb.WriteAccountingEntry(debit);
611 CAccountingEntry credit;
612 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
613 credit.strAccount = strTo;
614 credit.nCreditDebit = nAmount;
616 credit.strOtherAccount = strFrom;
617 credit.strComment = strComment;
618 walletdb.WriteAccountingEntry(credit);
620 if (!walletdb.TxnCommit())
621 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
627 Value sendfrom(const Array& params, bool fHelp)
629 if (fHelp || params.size() < 3 || params.size() > 6)
631 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
632 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
633 + HelpRequiringPassphrase());
635 string strAccount = AccountFromValue(params[0]);
636 CBitcoinAddress address(params[1].get_str());
637 if (!address.IsValid())
638 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
639 int64 nAmount = AmountFromValue(params[2]);
641 if (nAmount < MIN_TXOUT_AMOUNT)
642 throw JSONRPCError(-101, "Send amount too small");
645 if (params.size() > 3)
646 nMinDepth = params[3].get_int();
649 wtx.strFromAccount = strAccount;
650 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
651 wtx.mapValue["comment"] = params[4].get_str();
652 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
653 wtx.mapValue["to"] = params[5].get_str();
655 EnsureWalletIsUnlocked();
658 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
659 if (nAmount > nBalance)
660 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
663 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
665 throw JSONRPCError(RPC_WALLET_ERROR, strError);
667 return wtx.GetHash().GetHex();
671 Value sendmany(const Array& params, bool fHelp)
673 if (fHelp || params.size() < 2 || params.size() > 4)
675 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
676 "amounts are double-precision floating point numbers"
677 + HelpRequiringPassphrase());
679 string strAccount = AccountFromValue(params[0]);
680 Object sendTo = params[1].get_obj();
682 if (params.size() > 2)
683 nMinDepth = params[2].get_int();
686 wtx.strFromAccount = strAccount;
687 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
688 wtx.mapValue["comment"] = params[3].get_str();
690 set<CBitcoinAddress> setAddress;
691 vector<pair<CScript, int64> > vecSend;
693 int64 totalAmount = 0;
694 BOOST_FOREACH(const Pair& s, sendTo)
696 CBitcoinAddress address(s.name_);
697 if (!address.IsValid())
698 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
700 if (setAddress.count(address))
701 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
702 setAddress.insert(address);
704 CScript scriptPubKey;
705 scriptPubKey.SetDestination(address.Get());
706 int64 nAmount = AmountFromValue(s.value_);
708 if (nAmount < MIN_TXOUT_AMOUNT)
709 throw JSONRPCError(-101, "Send amount too small");
711 totalAmount += nAmount;
713 vecSend.push_back(make_pair(scriptPubKey, nAmount));
716 EnsureWalletIsUnlocked();
719 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
720 if (totalAmount > nBalance)
721 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
724 CReserveKey keyChange(pwalletMain);
725 int64 nFeeRequired = 0;
726 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
729 int64 nTotal = 0, nWatchOnly = 0;
730 pwalletMain->GetBalance(nTotal, nWatchOnly);
732 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
733 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
734 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
736 if (!pwalletMain->CommitTransaction(wtx, keyChange))
737 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
739 return wtx.GetHash().GetHex();
742 Value addmultisigaddress(const Array& params, bool fHelp)
744 if (fHelp || params.size() < 2 || params.size() > 3)
746 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
747 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
748 "each key is a NovaCoin address or hex-encoded public key\n"
749 "If [account] is specified, assign address to [account].";
750 throw runtime_error(msg);
753 int nRequired = params[0].get_int();
754 const Array& keys = params[1].get_array();
756 if (params.size() > 2)
757 strAccount = AccountFromValue(params[2]);
759 // Gather public keys
761 throw runtime_error("a multisignature address must require at least one key to redeem");
762 if ((int)keys.size() < nRequired)
764 strprintf("not enough keys supplied "
765 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
766 std::vector<CKey> pubkeys;
767 pubkeys.resize(keys.size());
768 for (unsigned int i = 0; i < keys.size(); i++)
770 const std::string& ks = keys[i].get_str();
772 // Case 1: Bitcoin address and we have full public key:
773 CBitcoinAddress address(ks);
774 if (address.IsValid())
777 if (!address.GetKeyID(keyID))
779 strprintf("%s does not refer to a key",ks.c_str()));
781 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
783 strprintf("no full public key for address %s",ks.c_str()));
784 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
785 throw runtime_error(" Invalid public key: "+ks);
788 // Case 2: hex public key
791 CPubKey vchPubKey(ParseHex(ks));
792 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
793 throw runtime_error(" Invalid public key: "+ks);
797 throw runtime_error(" Invalid public key: "+ks);
801 // Construct using pay-to-script-hash:
803 inner.SetMultisig(nRequired, pubkeys);
804 CScriptID innerID = inner.GetID();
805 pwalletMain->AddCScript(inner);
807 pwalletMain->SetAddressBookName(innerID, strAccount);
808 return CBitcoinAddress(innerID).ToString();
811 Value addredeemscript(const Array& params, bool fHelp)
813 if (fHelp || params.size() < 1 || params.size() > 2)
815 string msg = "addredeemscript <redeemScript> [account]\n"
816 "Add a P2SH address with a specified redeemScript to the wallet.\n"
817 "If [account] is specified, assign address to [account].";
818 throw runtime_error(msg);
822 if (params.size() > 1)
823 strAccount = AccountFromValue(params[1]);
825 // Construct using pay-to-script-hash:
826 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
827 CScript inner(innerData.begin(), innerData.end());
828 CScriptID innerID = inner.GetID();
829 pwalletMain->AddCScript(inner);
831 pwalletMain->SetAddressBookName(innerID, strAccount);
832 return CBitcoinAddress(innerID).ToString();
842 nConf = std::numeric_limits<int>::max();
846 Value ListReceived(const Array& params, bool fByAccounts)
848 // Minimum confirmations
850 if (params.size() > 0)
851 nMinDepth = params[0].get_int();
853 // Whether to include empty accounts
854 bool fIncludeEmpty = false;
855 if (params.size() > 1)
856 fIncludeEmpty = params[1].get_bool();
859 map<CBitcoinAddress, tallyitem> mapTally;
860 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
862 const CWalletTx& wtx = (*it).second;
864 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
867 int nDepth = wtx.GetDepthInMainChain();
868 if (nDepth < nMinDepth)
871 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
873 CTxDestination address;
874 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
877 tallyitem& item = mapTally[address];
878 item.nAmount += txout.nValue;
879 item.nConf = min(item.nConf, nDepth);
885 map<string, tallyitem> mapAccountTally;
886 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
888 const CBitcoinAddress& address = item.first;
889 const string& strAccount = item.second;
890 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
891 if (it == mapTally.end() && !fIncludeEmpty)
895 int nConf = std::numeric_limits<int>::max();
896 if (it != mapTally.end())
898 nAmount = (*it).second.nAmount;
899 nConf = (*it).second.nConf;
904 tallyitem& item = mapAccountTally[strAccount];
905 item.nAmount += nAmount;
906 item.nConf = min(item.nConf, nConf);
911 obj.push_back(Pair("address", address.ToString()));
912 obj.push_back(Pair("account", strAccount));
913 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
914 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
921 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
923 int64 nAmount = (*it).second.nAmount;
924 int nConf = (*it).second.nConf;
926 obj.push_back(Pair("account", (*it).first));
927 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
928 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
936 Value listreceivedbyaddress(const Array& params, bool fHelp)
938 if (fHelp || params.size() > 2)
940 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
941 "[minconf] is the minimum number of confirmations before payments are included.\n"
942 "[includeempty] whether to include addresses that haven't received any payments.\n"
943 "Returns an array of objects containing:\n"
944 " \"address\" : receiving address\n"
945 " \"account\" : the account of the receiving address\n"
946 " \"amount\" : total amount received by the address\n"
947 " \"confirmations\" : number of confirmations of the most recent transaction included");
949 return ListReceived(params, false);
952 Value listreceivedbyaccount(const Array& params, bool fHelp)
954 if (fHelp || params.size() > 2)
956 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
957 "[minconf] is the minimum number of confirmations before payments are included.\n"
958 "[includeempty] whether to include accounts that haven't received any payments.\n"
959 "Returns an array of objects containing:\n"
960 " \"account\" : the account of the receiving addresses\n"
961 " \"amount\" : total amount received by addresses with this account\n"
962 " \"confirmations\" : number of confirmations of the most recent transaction included");
964 return ListReceived(params, true);
967 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
969 CBitcoinAddress addr;
971 entry.push_back(Pair("address", addr.ToString()));
974 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
976 int64 nGeneratedImmature, nGeneratedMature, nFee;
977 string strSentAccount;
978 list<pair<CTxDestination, int64> > listReceived;
979 list<pair<CTxDestination, int64> > listSent;
981 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
983 bool fAllAccounts = (strAccount == string("*"));
985 // Generated blocks assigned to account ""
986 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
989 entry.push_back(Pair("account", string("")));
990 if (nGeneratedImmature)
992 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
993 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
997 entry.push_back(Pair("category", "generate"));
998 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1001 WalletTxToJSON(wtx, entry);
1002 ret.push_back(entry);
1006 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1008 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1011 entry.push_back(Pair("account", strSentAccount));
1012 MaybePushAddress(entry, s.first);
1014 if (wtx.GetDepthInMainChain() < 0) {
1015 entry.push_back(Pair("category", "conflicted"));
1017 entry.push_back(Pair("category", "send"));
1020 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1021 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1023 WalletTxToJSON(wtx, entry);
1024 ret.push_back(entry);
1029 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1031 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1034 if (pwalletMain->mapAddressBook.count(r.first))
1035 account = pwalletMain->mapAddressBook[r.first];
1036 if (fAllAccounts || (account == strAccount))
1039 entry.push_back(Pair("account", account));
1040 MaybePushAddress(entry, r.first);
1041 if (wtx.IsCoinBase())
1043 if (wtx.GetDepthInMainChain() < 1)
1044 entry.push_back(Pair("category", "orphan"));
1045 else if (wtx.GetBlocksToMaturity() > 0)
1046 entry.push_back(Pair("category", "immature"));
1048 entry.push_back(Pair("category", "generate"));
1051 entry.push_back(Pair("category", "receive"));
1052 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1054 WalletTxToJSON(wtx, entry);
1055 ret.push_back(entry);
1061 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1063 bool fAllAccounts = (strAccount == string("*"));
1065 if (fAllAccounts || acentry.strAccount == strAccount)
1068 entry.push_back(Pair("account", acentry.strAccount));
1069 entry.push_back(Pair("category", "move"));
1070 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1071 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1072 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1073 entry.push_back(Pair("comment", acentry.strComment));
1074 ret.push_back(entry);
1078 Value listtransactions(const Array& params, bool fHelp)
1080 if (fHelp || params.size() > 3)
1081 throw runtime_error(
1082 "listtransactions [account] [count=10] [from=0]\n"
1083 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1085 string strAccount = "*";
1086 if (params.size() > 0)
1087 strAccount = params[0].get_str();
1089 if (params.size() > 1)
1090 nCount = params[1].get_int();
1092 if (params.size() > 2)
1093 nFrom = params[2].get_int();
1096 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1098 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1102 std::list<CAccountingEntry> acentries;
1103 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1105 // iterate backwards until we have nCount items to return:
1106 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1108 CWalletTx *const pwtx = (*it).second.first;
1110 ListTransactions(*pwtx, strAccount, 0, true, ret);
1111 CAccountingEntry *const pacentry = (*it).second.second;
1113 AcentryToJSON(*pacentry, strAccount, ret);
1115 if ((int)ret.size() >= (nCount+nFrom)) break;
1117 // ret is newest to oldest
1119 if (nFrom > (int)ret.size())
1121 if ((nFrom + nCount) > (int)ret.size())
1122 nCount = ret.size() - nFrom;
1123 Array::iterator first = ret.begin();
1124 std::advance(first, nFrom);
1125 Array::iterator last = ret.begin();
1126 std::advance(last, nFrom+nCount);
1128 if (last != ret.end()) ret.erase(last, ret.end());
1129 if (first != ret.begin()) ret.erase(ret.begin(), first);
1131 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1136 Value listaccounts(const Array& params, bool fHelp)
1138 if (fHelp || params.size() > 1)
1139 throw runtime_error(
1140 "listaccounts [minconf=1]\n"
1141 "Returns Object that has account names as keys, account balances as values.");
1144 if (params.size() > 0)
1145 nMinDepth = params[0].get_int();
1147 map<string, int64> mapAccountBalances;
1148 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1149 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1150 mapAccountBalances[entry.second] = 0;
1153 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1155 const CWalletTx& wtx = (*it).second;
1156 int64 nGeneratedImmature, nGeneratedMature, nFee;
1157 string strSentAccount;
1158 list<pair<CTxDestination, int64> > listReceived;
1159 list<pair<CTxDestination, int64> > listSent;
1160 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1161 mapAccountBalances[strSentAccount] -= nFee;
1162 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1163 mapAccountBalances[strSentAccount] -= s.second;
1164 if (wtx.GetDepthInMainChain() >= nMinDepth)
1166 mapAccountBalances[""] += nGeneratedMature;
1167 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1168 if (pwalletMain->mapAddressBook.count(r.first))
1169 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1171 mapAccountBalances[""] += r.second;
1175 list<CAccountingEntry> acentries;
1176 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1177 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1178 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1181 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1182 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1187 Value listsinceblock(const Array& params, bool fHelp)
1190 throw runtime_error(
1191 "listsinceblock [blockhash] [target-confirmations]\n"
1192 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1194 CBlockIndex *pindex = NULL;
1195 int target_confirms = 1;
1197 if (params.size() > 0)
1199 uint256 blockId = 0;
1201 blockId.SetHex(params[0].get_str());
1202 pindex = CBlockLocator(blockId).GetBlockIndex();
1205 if (params.size() > 1)
1207 target_confirms = params[1].get_int();
1209 if (target_confirms < 1)
1210 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1213 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1217 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1219 CWalletTx tx = (*it).second;
1221 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1222 ListTransactions(tx, "*", 0, true, transactions);
1227 if (target_confirms == 1)
1229 lastblock = hashBestChain;
1233 int target_height = pindexBest->nHeight + 1 - target_confirms;
1236 for (block = pindexBest;
1237 block && block->nHeight > target_height;
1238 block = block->pprev) { }
1240 lastblock = block ? block->GetBlockHash() : 0;
1244 ret.push_back(Pair("transactions", transactions));
1245 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1250 Value gettransaction(const Array& params, bool fHelp)
1252 if (fHelp || params.size() != 1)
1253 throw runtime_error(
1254 "gettransaction <txid>\n"
1255 "Get detailed information about <txid>");
1258 hash.SetHex(params[0].get_str());
1262 if (pwalletMain->mapWallet.count(hash))
1264 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1266 TxToJSON(wtx, 0, entry);
1268 int64 nCredit = wtx.GetCredit(false);
1269 int64 nDebit = wtx.GetDebit();
1270 int64 nNet = nCredit - nDebit;
1271 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1273 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1275 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1277 WalletTxToJSON(wtx, entry);
1280 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1281 entry.push_back(Pair("details", details));
1286 uint256 hashBlock = 0;
1287 if (GetTransaction(hash, tx, hashBlock))
1289 TxToJSON(tx, 0, entry);
1291 entry.push_back(Pair("confirmations", 0));
1294 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1295 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1296 if (mi != mapBlockIndex.end() && (*mi).second)
1298 CBlockIndex* pindex = (*mi).second;
1299 if (pindex->IsInMainChain())
1300 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1302 entry.push_back(Pair("confirmations", 0));
1307 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1314 Value backupwallet(const Array& params, bool fHelp)
1316 if (fHelp || params.size() != 1)
1317 throw runtime_error(
1318 "backupwallet <destination>\n"
1319 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1321 string strDest = params[0].get_str();
1322 if (!BackupWallet(*pwalletMain, strDest))
1323 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1329 Value keypoolrefill(const Array& params, bool fHelp)
1331 if (fHelp || params.size() > 1)
1332 throw runtime_error(
1333 "keypoolrefill [new-size]\n"
1334 "Fills the keypool."
1335 + HelpRequiringPassphrase());
1337 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1338 if (params.size() > 0) {
1339 if (params[0].get_int() < 0)
1340 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1341 nSize = (unsigned int) params[0].get_int();
1344 EnsureWalletIsUnlocked();
1346 pwalletMain->TopUpKeyPool(nSize);
1348 if (pwalletMain->GetKeyPoolSize() < nSize)
1349 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1355 void ThreadTopUpKeyPool(void* parg)
1357 // Make this thread recognisable as the key-topping-up thread
1358 RenameThread("novacoin-key-top");
1360 pwalletMain->TopUpKeyPool();
1363 void ThreadCleanWalletPassphrase(void* parg)
1365 // Make this thread recognisable as the wallet relocking thread
1366 RenameThread("novacoin-lock-wa");
1368 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1370 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1372 if (nWalletUnlockTime == 0)
1374 nWalletUnlockTime = nMyWakeTime;
1378 if (nWalletUnlockTime==0)
1380 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1384 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1386 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1390 if (nWalletUnlockTime)
1392 nWalletUnlockTime = 0;
1393 pwalletMain->Lock();
1398 if (nWalletUnlockTime < nMyWakeTime)
1399 nWalletUnlockTime = nMyWakeTime;
1402 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1404 delete (int64*)parg;
1407 Value walletpassphrase(const Array& params, bool fHelp)
1409 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1410 throw runtime_error(
1411 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1412 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1413 "mintonly is optional true/false allowing only block minting.");
1416 if (!pwalletMain->IsCrypted())
1417 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1419 if (!pwalletMain->IsLocked())
1420 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1421 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1422 SecureString strWalletPass;
1423 strWalletPass.reserve(100);
1424 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1425 // Alternately, find a way to make params[0] mlock()'d to begin with.
1426 strWalletPass = params[0].get_str().c_str();
1428 if (strWalletPass.length() > 0)
1430 if (!pwalletMain->Unlock(strWalletPass))
1431 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1434 throw runtime_error(
1435 "walletpassphrase <passphrase> <timeout>\n"
1436 "Stores the wallet decryption key in memory for <timeout> seconds.");
1438 NewThread(ThreadTopUpKeyPool, NULL);
1439 int64* pnSleepTime = new int64(params[1].get_int64());
1440 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1442 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1443 if (params.size() > 2)
1444 fWalletUnlockMintOnly = params[2].get_bool();
1446 fWalletUnlockMintOnly = false;
1452 Value walletpassphrasechange(const Array& params, bool fHelp)
1454 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1455 throw runtime_error(
1456 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1457 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1460 if (!pwalletMain->IsCrypted())
1461 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1463 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1464 // Alternately, find a way to make params[0] mlock()'d to begin with.
1465 SecureString strOldWalletPass;
1466 strOldWalletPass.reserve(100);
1467 strOldWalletPass = params[0].get_str().c_str();
1469 SecureString strNewWalletPass;
1470 strNewWalletPass.reserve(100);
1471 strNewWalletPass = params[1].get_str().c_str();
1473 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1474 throw runtime_error(
1475 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1476 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1478 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1479 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1485 Value walletlock(const Array& params, bool fHelp)
1487 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1488 throw runtime_error(
1490 "Removes the wallet encryption key from memory, locking the wallet.\n"
1491 "After calling this method, you will need to call walletpassphrase again\n"
1492 "before being able to call any methods which require the wallet to be unlocked.");
1495 if (!pwalletMain->IsCrypted())
1496 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1499 LOCK(cs_nWalletUnlockTime);
1500 pwalletMain->Lock();
1501 nWalletUnlockTime = 0;
1508 Value encryptwallet(const Array& params, bool fHelp)
1510 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1511 throw runtime_error(
1512 "encryptwallet <passphrase>\n"
1513 "Encrypts the wallet with <passphrase>.");
1516 if (pwalletMain->IsCrypted())
1517 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1519 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1520 // Alternately, find a way to make params[0] mlock()'d to begin with.
1521 SecureString strWalletPass;
1522 strWalletPass.reserve(100);
1523 strWalletPass = params[0].get_str().c_str();
1525 if (strWalletPass.length() < 1)
1526 throw runtime_error(
1527 "encryptwallet <passphrase>\n"
1528 "Encrypts the wallet with <passphrase>.");
1530 if (!pwalletMain->EncryptWallet(strWalletPass))
1531 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1533 // BDB seems to have a bad habit of writing old data into
1534 // slack space in .dat files; that is bad if the old data is
1535 // unencrypted private keys. So:
1537 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1540 class DescribeAddressVisitor : public boost::static_visitor<Object>
1545 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1547 Object operator()(const CNoDestination &dest) const { return Object(); }
1548 Object operator()(const CKeyID &keyID) const {
1551 pwalletMain->GetPubKey(keyID, vchPubKey);
1552 obj.push_back(Pair("isscript", false));
1553 if (mine == MINE_SPENDABLE) {
1554 pwalletMain->GetPubKey(keyID, vchPubKey);
1555 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1556 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1561 Object operator()(const CScriptID &scriptID) const {
1563 obj.push_back(Pair("isscript", true));
1564 if (mine == MINE_SPENDABLE) {
1566 pwalletMain->GetCScript(scriptID, subscript);
1567 std::vector<CTxDestination> addresses;
1568 txnouttype whichType;
1570 ExtractDestinations(subscript, whichType, addresses, nRequired);
1571 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1572 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1574 BOOST_FOREACH(const CTxDestination& addr, addresses)
1575 a.push_back(CBitcoinAddress(addr).ToString());
1576 obj.push_back(Pair("addresses", a));
1577 if (whichType == TX_MULTISIG)
1578 obj.push_back(Pair("sigsrequired", nRequired));
1584 Value validateaddress(const Array& params, bool fHelp)
1586 if (fHelp || params.size() != 1)
1587 throw runtime_error(
1588 "validateaddress <novacoinaddress>\n"
1589 "Return information about <novacoinaddress>.");
1591 CBitcoinAddress address(params[0].get_str());
1592 bool isValid = address.IsValid();
1595 ret.push_back(Pair("isvalid", isValid));
1598 CTxDestination dest = address.Get();
1599 string currentAddress = address.ToString();
1600 ret.push_back(Pair("address", currentAddress));
1601 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1602 ret.push_back(Pair("ismine", mine != MINE_NO));
1603 if (mine != MINE_NO) {
1604 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1605 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1606 ret.insert(ret.end(), detail.begin(), detail.end());
1608 if (pwalletMain->mapAddressBook.count(dest))
1609 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1614 // ppcoin: reserve balance from being staked for network protection
1615 Value reservebalance(const Array& params, bool fHelp)
1617 if (fHelp || params.size() > 2)
1618 throw runtime_error(
1619 "reservebalance [<reserve> [amount]]\n"
1620 "<reserve> is true or false to turn balance reserve on or off.\n"
1621 "<amount> is a real and rounded to cent.\n"
1622 "Set reserve amount not participating in network protection.\n"
1623 "If no parameters provided current setting is printed.\n");
1625 if (params.size() > 0)
1627 bool fReserve = params[0].get_bool();
1630 if (params.size() == 1)
1631 throw runtime_error("must provide amount to reserve balance.\n");
1632 int64 nAmount = AmountFromValue(params[1]);
1633 nAmount = (nAmount / CENT) * CENT; // round to cent
1635 throw runtime_error("amount cannot be negative.\n");
1636 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1640 if (params.size() > 1)
1641 throw runtime_error("cannot specify amount to turn off reserve.\n");
1642 mapArgs["-reservebalance"] = "0";
1647 int64 nReserveBalance = 0;
1648 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1649 throw runtime_error("invalid reserve balance amount\n");
1650 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1651 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1656 // ppcoin: check wallet integrity
1657 Value checkwallet(const Array& params, bool fHelp)
1659 if (fHelp || params.size() > 0)
1660 throw runtime_error(
1662 "Check wallet for integrity.\n");
1665 int64 nBalanceInQuestion;
1666 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1668 if (nMismatchSpent == 0)
1669 result.push_back(Pair("wallet check passed", true));
1672 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1673 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1679 // ppcoin: repair wallet
1680 Value repairwallet(const Array& params, bool fHelp)
1682 if (fHelp || params.size() > 0)
1683 throw runtime_error(
1685 "Repair wallet if checkwallet reports any problem.\n");
1688 int64 nBalanceInQuestion;
1689 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1691 if (nMismatchSpent == 0)
1692 result.push_back(Pair("wallet check passed", true));
1695 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1696 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1701 // NovaCoin: resend unconfirmed wallet transactions
1702 Value resendtx(const Array& params, bool fHelp)
1704 if (fHelp || params.size() > 1)
1705 throw runtime_error(
1707 "Re-send unconfirmed transactions.\n"
1710 ResendWalletTransactions();
1715 // ppcoin: make a public-private key pair
1716 Value makekeypair(const Array& params, bool fHelp)
1718 if (fHelp || params.size() > 1)
1719 throw runtime_error(
1720 "makekeypair [prefix]\n"
1721 "Make a public/private key pair.\n"
1722 "[prefix] is optional preferred prefix for the public key.\n");
1724 string strPrefix = "";
1725 if (params.size() > 0)
1726 strPrefix = params[0].get_str();
1729 key.MakeNewKey(false);
1731 CPrivKey vchPrivKey = key.GetPrivKey();
1733 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1734 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));