raw
genesis                 1 // Copyright (c) 2010 Satoshi Nakamoto
genesis 2 // Copyright (c) 2009-2012 The Bitcoin developers
genesis 3 // Distributed under the MIT/X11 software license, see the accompanying
genesis 4 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
genesis 5
genesis 6 #include "headers.h"
genesis 7 #include "db.h"
genesis 8 #include "net.h"
genesis 9 #include "init.h"
genesis 10 #undef printf
genesis 11 #include <boost/asio.hpp>
genesis 12 #include <boost/iostreams/concepts.hpp>
genesis 13 #include <boost/iostreams/stream.hpp>
genesis 14 #include <boost/algorithm/string.hpp>
genesis 15 #ifdef USE_SSL
genesis 16 #include <boost/asio/ssl.hpp>
genesis 17 #include <boost/filesystem.hpp>
genesis 18 #include <boost/filesystem/fstream.hpp>
genesis 19 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
genesis 20 #endif
genesis 21 #include "json/json_spirit_reader_template.h"
genesis 22 #include "json/json_spirit_writer_template.h"
genesis 23 #include "json/json_spirit_utils.h"
genesis 24 #define printf OutputDebugStringF
genesis 25 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
genesis 26 // precompiled in headers.h. The problem might be when the pch file goes over
genesis 27 // a certain size around 145MB. If we need access to json_spirit outside this
genesis 28 // file, we could use the compiled json_spirit option.
genesis 29
genesis 30 using namespace std;
genesis 31 using namespace boost;
genesis 32 using namespace boost::asio;
genesis 33 using namespace json_spirit;
genesis 34
genesis 35 void ThreadRPCServer2(void* parg);
genesis 36 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
genesis 37 extern map<string, rpcfn_type> mapCallTable;
genesis 38
genesis 39 static std::string strRPCUserColonPass;
genesis 40
genesis 41 static int64 nWalletUnlockTime;
genesis 42 static CCriticalSection cs_nWalletUnlockTime;
genesis 43
genesis 44
genesis 45 Object JSONRPCError(int code, const string& message)
genesis 46 {
genesis 47 Object error;
genesis 48 error.push_back(Pair("code", code));
genesis 49 error.push_back(Pair("message", message));
genesis 50 return error;
genesis 51 }
genesis 52
genesis 53
genesis 54 void PrintConsole(const std::string &format, ...)
genesis 55 {
genesis 56 char buffer[50000];
genesis 57 int limit = sizeof(buffer);
genesis 58 va_list arg_ptr;
genesis 59 va_start(arg_ptr, format);
genesis 60 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
genesis 61 va_end(arg_ptr);
genesis 62 if (ret < 0 || ret >= limit)
genesis 63 {
genesis 64 ret = limit - 1;
genesis 65 buffer[limit-1] = 0;
genesis 66 }
genesis 67 printf("%s", buffer);
genesis 68 fprintf(stdout, "%s", buffer);
genesis 69 }
genesis 70
genesis 71
genesis 72 int64 AmountFromValue(const Value& value)
genesis 73 {
genesis 74 double dAmount = value.get_real();
genesis 75 if (dAmount <= 0.0 || dAmount > 21000000.0)
genesis 76 throw JSONRPCError(-3, "Invalid amount");
genesis 77 int64 nAmount = roundint64(dAmount * COIN);
genesis 78 if (!MoneyRange(nAmount))
genesis 79 throw JSONRPCError(-3, "Invalid amount");
genesis 80 return nAmount;
genesis 81 }
genesis 82
genesis 83 Value ValueFromAmount(int64 amount)
genesis 84 {
genesis 85 return (double)amount / (double)COIN;
genesis 86 }
genesis 87
genesis 88 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
genesis 89 {
genesis 90 entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
genesis 91 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
genesis 92 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
genesis 93 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
genesis 94 entry.push_back(Pair(item.first, item.second));
genesis 95 }
genesis 96
genesis 97 string AccountFromValue(const Value& value)
genesis 98 {
genesis 99 string strAccount = value.get_str();
genesis 100 if (strAccount == "*")
genesis 101 throw JSONRPCError(-11, "Invalid account name");
genesis 102 return strAccount;
genesis 103 }
genesis 104
genesis 105
genesis 106
genesis 107 ///
genesis 108 /// Note: This interface may still be subject to change.
genesis 109 ///
genesis 110
genesis 111
genesis 112 Value help(const Array& params, bool fHelp)
genesis 113 {
genesis 114 if (fHelp || params.size() > 1)
genesis 115 throw runtime_error(
genesis 116 "help [command]\n"
genesis 117 "List commands, or get help for a command.");
genesis 118
genesis 119 string strCommand;
genesis 120 if (params.size() > 0)
genesis 121 strCommand = params[0].get_str();
genesis 122
genesis 123 string strRet;
genesis 124 set<rpcfn_type> setDone;
genesis 125 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
genesis 126 {
genesis 127 string strMethod = (*mi).first;
genesis 128 // We already filter duplicates, but these deprecated screw up the sort order
genesis 129 if (strMethod == "getamountreceived" ||
genesis 130 strMethod == "getallreceived" ||
genesis 131 strMethod == "getblocknumber" || // deprecated
genesis 132 (strMethod.find("label") != string::npos))
genesis 133 continue;
genesis 134 if (strCommand != "" && strMethod != strCommand)
genesis 135 continue;
genesis 136 try
genesis 137 {
genesis 138 Array params;
genesis 139 rpcfn_type pfn = (*mi).second;
genesis 140 if (setDone.insert(pfn).second)
genesis 141 (*pfn)(params, true);
genesis 142 }
genesis 143 catch (std::exception& e)
genesis 144 {
genesis 145 // Help text is returned in an exception
genesis 146 string strHelp = string(e.what());
genesis 147 if (strCommand == "")
genesis 148 if (strHelp.find('\n') != -1)
genesis 149 strHelp = strHelp.substr(0, strHelp.find('\n'));
genesis 150 strRet += strHelp + "\n";
genesis 151 }
genesis 152 }
genesis 153 if (strRet == "")
genesis 154 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
genesis 155 strRet = strRet.substr(0,strRet.size()-1);
genesis 156 return strRet;
genesis 157 }
genesis 158
genesis 159
genesis 160 Value stop(const Array& params, bool fHelp)
genesis 161 {
genesis 162 if (fHelp || params.size() != 0)
genesis 163 throw runtime_error(
genesis 164 "stop\n"
genesis 165 "Stop bitcoin server.");
genesis 166 #ifndef QT_GUI
genesis 167 // Shutdown will take long enough that the response should get back
genesis 168 CreateThread(Shutdown, NULL);
genesis 169 return "bitcoin server stopping";
genesis 170 #else
genesis 171 throw runtime_error("NYI: cannot shut down GUI with RPC command");
genesis 172 #endif
genesis 173 }
genesis 174
genesis 175
genesis 176 Value getblockcount(const Array& params, bool fHelp)
genesis 177 {
genesis 178 if (fHelp || params.size() != 0)
genesis 179 throw runtime_error(
genesis 180 "getblockcount\n"
genesis 181 "Returns the number of blocks in the longest block chain.");
genesis 182
genesis 183 return nBestHeight;
genesis 184 }
genesis 185
genesis 186
genesis 187 // deprecated
genesis 188 Value getblocknumber(const Array& params, bool fHelp)
genesis 189 {
genesis 190 if (fHelp || params.size() != 0)
genesis 191 throw runtime_error(
genesis 192 "getblocknumber\n"
genesis 193 "Deprecated. Use getblockcount.");
genesis 194
genesis 195 return nBestHeight;
genesis 196 }
genesis 197
genesis 198
genesis 199 Value getconnectioncount(const Array& params, bool fHelp)
genesis 200 {
genesis 201 if (fHelp || params.size() != 0)
genesis 202 throw runtime_error(
genesis 203 "getconnectioncount\n"
genesis 204 "Returns the number of connections to other nodes.");
genesis 205
genesis 206 return (int)vNodes.size();
genesis 207 }
genesis 208
genesis 209
genesis 210 double GetDifficulty()
genesis 211 {
genesis 212 // Floating point number that is a multiple of the minimum difficulty,
genesis 213 // minimum difficulty = 1.0.
genesis 214
genesis 215 if (pindexBest == NULL)
genesis 216 return 1.0;
genesis 217 int nShift = (pindexBest->nBits >> 24) & 0xff;
genesis 218
genesis 219 double dDiff =
genesis 220 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
genesis 221
genesis 222 while (nShift < 29)
genesis 223 {
genesis 224 dDiff *= 256.0;
genesis 225 nShift++;
genesis 226 }
genesis 227 while (nShift > 29)
genesis 228 {
genesis 229 dDiff /= 256.0;
genesis 230 nShift--;
genesis 231 }
genesis 232
genesis 233 return dDiff;
genesis 234 }
genesis 235
genesis 236 Value getdifficulty(const Array& params, bool fHelp)
genesis 237 {
genesis 238 if (fHelp || params.size() != 0)
genesis 239 throw runtime_error(
genesis 240 "getdifficulty\n"
genesis 241 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
genesis 242
genesis 243 return GetDifficulty();
genesis 244 }
genesis 245
genesis 246
genesis 247 Value getgenerate(const Array& params, bool fHelp)
genesis 248 {
genesis 249 if (fHelp || params.size() != 0)
genesis 250 throw runtime_error(
genesis 251 "getgenerate\n"
genesis 252 "Returns true or false.");
genesis 253
genesis 254 return (bool)fGenerateBitcoins;
genesis 255 }
genesis 256
genesis 257
genesis 258 Value setgenerate(const Array& params, bool fHelp)
genesis 259 {
genesis 260 if (fHelp || params.size() < 1 || params.size() > 2)
genesis 261 throw runtime_error(
genesis 262 "setgenerate <generate> [genproclimit]\n"
genesis 263 "<generate> is true or false to turn generation on or off.\n"
genesis 264 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
genesis 265
genesis 266 bool fGenerate = true;
genesis 267 if (params.size() > 0)
genesis 268 fGenerate = params[0].get_bool();
genesis 269
genesis 270 if (params.size() > 1)
genesis 271 {
genesis 272 int nGenProcLimit = params[1].get_int();
genesis 273 fLimitProcessors = (nGenProcLimit != -1);
genesis 274 WriteSetting("fLimitProcessors", fLimitProcessors);
genesis 275 if (nGenProcLimit != -1)
genesis 276 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
genesis 277 if (nGenProcLimit == 0)
genesis 278 fGenerate = false;
genesis 279 }
genesis 280
genesis 281 GenerateBitcoins(fGenerate, pwalletMain);
genesis 282 return Value::null;
genesis 283 }
genesis 284
genesis 285
genesis 286 Value gethashespersec(const Array& params, bool fHelp)
genesis 287 {
genesis 288 if (fHelp || params.size() != 0)
genesis 289 throw runtime_error(
genesis 290 "gethashespersec\n"
genesis 291 "Returns a recent hashes per second performance measurement while generating.");
genesis 292
genesis 293 if (GetTimeMillis() - nHPSTimerStart > 8000)
genesis 294 return (boost::int64_t)0;
genesis 295 return (boost::int64_t)dHashesPerSec;
genesis 296 }
genesis 297
genesis 298
genesis 299 Value getinfo(const Array& params, bool fHelp)
genesis 300 {
genesis 301 if (fHelp || params.size() != 0)
genesis 302 throw runtime_error(
genesis 303 "getinfo\n"
genesis 304 "Returns an object containing various state info.");
genesis 305
genesis 306 Object obj;
genesis 307 obj.push_back(Pair("version", (int)VERSION));
genesis 308 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
genesis 309 obj.push_back(Pair("blocks", (int)nBestHeight));
genesis 310 obj.push_back(Pair("connections", (int)vNodes.size()));
genesis 311 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
genesis 312 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
genesis 313 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
genesis 314 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
genesis 315 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
genesis 316 obj.push_back(Pair("testnet", fTestNet));
genesis 317 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
genesis 318 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
genesis 319 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
genesis 320 if (pwalletMain->IsCrypted())
genesis 321 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
genesis 322 obj.push_back(Pair("errors", GetWarnings("statusbar")));
genesis 323 return obj;
genesis 324 }
genesis 325
genesis 326
genesis 327 Value getnewaddress(const Array& params, bool fHelp)
genesis 328 {
genesis 329 if (fHelp || params.size() > 1)
genesis 330 throw runtime_error(
genesis 331 "getnewaddress [account]\n"
genesis 332 "Returns a new bitcoin address for receiving payments. "
genesis 333 "If [account] is specified (recommended), it is added to the address book "
genesis 334 "so payments received with the address will be credited to [account].");
genesis 335
genesis 336 // Parse the account first so we don't generate a key if there's an error
genesis 337 string strAccount;
genesis 338 if (params.size() > 0)
genesis 339 strAccount = AccountFromValue(params[0]);
genesis 340
genesis 341 if (!pwalletMain->IsLocked())
genesis 342 pwalletMain->TopUpKeyPool();
genesis 343
genesis 344 // Generate a new key that is added to wallet
genesis 345 std::vector<unsigned char> newKey;
genesis 346 if (!pwalletMain->GetKeyFromPool(newKey, false))
genesis 347 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
genesis 348 CBitcoinAddress address(newKey);
genesis 349
genesis 350 pwalletMain->SetAddressBookName(address, strAccount);
genesis 351
genesis 352 return address.ToString();
genesis 353 }
genesis 354
genesis 355
genesis 356 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
genesis 357 {
genesis 358 CWalletDB walletdb(pwalletMain->strWalletFile);
genesis 359
genesis 360 CAccount account;
genesis 361 walletdb.ReadAccount(strAccount, account);
genesis 362
genesis 363 bool bKeyUsed = false;
genesis 364
genesis 365 // Check if the current key has been used
genesis 366 if (!account.vchPubKey.empty())
genesis 367 {
genesis 368 CScript scriptPubKey;
genesis 369 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
genesis 370 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
genesis 371 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
genesis 372 ++it)
genesis 373 {
genesis 374 const CWalletTx& wtx = (*it).second;
genesis 375 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
genesis 376 if (txout.scriptPubKey == scriptPubKey)
genesis 377 bKeyUsed = true;
genesis 378 }
genesis 379 }
genesis 380
genesis 381 // Generate a new key
genesis 382 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
genesis 383 {
genesis 384 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
genesis 385 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
genesis 386
genesis 387 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
genesis 388 walletdb.WriteAccount(strAccount, account);
genesis 389 }
genesis 390
genesis 391 return CBitcoinAddress(account.vchPubKey);
genesis 392 }
genesis 393
genesis 394 Value getaccountaddress(const Array& params, bool fHelp)
genesis 395 {
genesis 396 if (fHelp || params.size() != 1)
genesis 397 throw runtime_error(
genesis 398 "getaccountaddress <account>\n"
genesis 399 "Returns the current bitcoin address for receiving payments to this account.");
genesis 400
genesis 401 // Parse the account first so we don't generate a key if there's an error
genesis 402 string strAccount = AccountFromValue(params[0]);
genesis 403
genesis 404 Value ret;
genesis 405
genesis 406 ret = GetAccountAddress(strAccount).ToString();
genesis 407
genesis 408 return ret;
genesis 409 }
genesis 410
genesis 411
genesis 412
genesis 413 Value setaccount(const Array& params, bool fHelp)
genesis 414 {
genesis 415 if (fHelp || params.size() < 1 || params.size() > 2)
genesis 416 throw runtime_error(
genesis 417 "setaccount <bitcoinaddress> <account>\n"
genesis 418 "Sets the account associated with the given address.");
genesis 419
genesis 420 CBitcoinAddress address(params[0].get_str());
genesis 421 if (!address.IsValid())
genesis 422 throw JSONRPCError(-5, "Invalid bitcoin address");
genesis 423
genesis 424
genesis 425 string strAccount;
genesis 426 if (params.size() > 1)
genesis 427 strAccount = AccountFromValue(params[1]);
genesis 428
genesis 429 // Detect when changing the account of an address that is the 'unused current key' of another account:
genesis 430 if (pwalletMain->mapAddressBook.count(address))
genesis 431 {
genesis 432 string strOldAccount = pwalletMain->mapAddressBook[address];
genesis 433 if (address == GetAccountAddress(strOldAccount))
genesis 434 GetAccountAddress(strOldAccount, true);
genesis 435 }
genesis 436
genesis 437 pwalletMain->SetAddressBookName(address, strAccount);
genesis 438
genesis 439 return Value::null;
genesis 440 }
genesis 441
genesis 442
genesis 443 Value getaccount(const Array& params, bool fHelp)
genesis 444 {
genesis 445 if (fHelp || params.size() != 1)
genesis 446 throw runtime_error(
genesis 447 "getaccount <bitcoinaddress>\n"
genesis 448 "Returns the account associated with the given address.");
genesis 449
genesis 450 CBitcoinAddress address(params[0].get_str());
genesis 451 if (!address.IsValid())
genesis 452 throw JSONRPCError(-5, "Invalid bitcoin address");
genesis 453
genesis 454 string strAccount;
genesis 455 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
genesis 456 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
genesis 457 strAccount = (*mi).second;
genesis 458 return strAccount;
genesis 459 }
genesis 460
genesis 461
genesis 462 Value getaddressesbyaccount(const Array& params, bool fHelp)
genesis 463 {
genesis 464 if (fHelp || params.size() != 1)
genesis 465 throw runtime_error(
genesis 466 "getaddressesbyaccount <account>\n"
genesis 467 "Returns the list of addresses for the given account.");
genesis 468
genesis 469 string strAccount = AccountFromValue(params[0]);
genesis 470
genesis 471 // Find all addresses that have the given account
genesis 472 Array ret;
genesis 473 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
genesis 474 {
genesis 475 const CBitcoinAddress& address = item.first;
genesis 476 const string& strName = item.second;
genesis 477 if (strName == strAccount)
genesis 478 ret.push_back(address.ToString());
genesis 479 }
genesis 480 return ret;
genesis 481 }
genesis 482
genesis 483 Value settxfee(const Array& params, bool fHelp)
genesis 484 {
genesis 485 if (fHelp || params.size() < 1 || params.size() > 1)
genesis 486 throw runtime_error(
genesis 487 "settxfee <amount>\n"
genesis 488 "<amount> is a real and is rounded to the nearest 0.00000001");
genesis 489
genesis 490 // Amount
genesis 491 int64 nAmount = 0;
genesis 492 if (params[0].get_real() != 0.0)
genesis 493 nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
genesis 494
genesis 495 nTransactionFee = nAmount;
genesis 496 return true;
genesis 497 }
genesis 498
genesis 499 Value sendtoaddress(const Array& params, bool fHelp)
genesis 500 {
genesis 501 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
genesis 502 throw runtime_error(
genesis 503 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
genesis 504 "<amount> is a real and is rounded to the nearest 0.00000001\n"
genesis 505 "requires wallet passphrase to be set with walletpassphrase first");
genesis 506 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
genesis 507 throw runtime_error(
genesis 508 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
genesis 509 "<amount> is a real and is rounded to the nearest 0.00000001");
genesis 510
genesis 511 CBitcoinAddress address(params[0].get_str());
genesis 512 if (!address.IsValid())
genesis 513 throw JSONRPCError(-5, "Invalid bitcoin address");
genesis 514
genesis 515 // Amount
genesis 516 int64 nAmount = AmountFromValue(params[1]);
genesis 517
genesis 518 // Wallet comments
genesis 519 CWalletTx wtx;
genesis 520 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
genesis 521 wtx.mapValue["comment"] = params[2].get_str();
genesis 522 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
genesis 523 wtx.mapValue["to"] = params[3].get_str();
genesis 524
genesis 525 if (pwalletMain->IsLocked())
genesis 526 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
genesis 527
genesis 528 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
genesis 529 if (strError != "")
genesis 530 throw JSONRPCError(-4, strError);
genesis 531
genesis 532 return wtx.GetHash().GetHex();
genesis 533 }
genesis 534
genesis 535 static const string strMessageMagic = "Bitcoin Signed Message:\n";
genesis 536
genesis 537 Value signmessage(const Array& params, bool fHelp)
genesis 538 {
genesis 539 if (fHelp || params.size() != 2)
genesis 540 throw runtime_error(
genesis 541 "signmessage <bitcoinaddress> <message>\n"
genesis 542 "Sign a message with the private key of an address");
genesis 543
genesis 544 if (pwalletMain->IsLocked())
genesis 545 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
genesis 546
genesis 547 string strAddress = params[0].get_str();
genesis 548 string strMessage = params[1].get_str();
genesis 549
genesis 550 CBitcoinAddress addr(strAddress);
genesis 551 if (!addr.IsValid())
genesis 552 throw JSONRPCError(-3, "Invalid address");
genesis 553
genesis 554 CKey key;
genesis 555 if (!pwalletMain->GetKey(addr, key))
genesis 556 throw JSONRPCError(-4, "Private key not available");
genesis 557
genesis 558 CDataStream ss(SER_GETHASH);
genesis 559 ss << strMessageMagic;
genesis 560 ss << strMessage;
genesis 561
genesis 562 vector<unsigned char> vchSig;
genesis 563 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
genesis 564 throw JSONRPCError(-5, "Sign failed");
genesis 565
genesis 566 return EncodeBase64(&vchSig[0], vchSig.size());
genesis 567 }
genesis 568
genesis 569 Value verifymessage(const Array& params, bool fHelp)
genesis 570 {
genesis 571 if (fHelp || params.size() != 3)
genesis 572 throw runtime_error(
genesis 573 "verifymessage <bitcoinaddress> <signature> <message>\n"
genesis 574 "Verify a signed message");
genesis 575
genesis 576 string strAddress = params[0].get_str();
genesis 577 string strSign = params[1].get_str();
genesis 578 string strMessage = params[2].get_str();
genesis 579
genesis 580 CBitcoinAddress addr(strAddress);
genesis 581 if (!addr.IsValid())
genesis 582 throw JSONRPCError(-3, "Invalid address");
genesis 583
genesis 584 bool fInvalid = false;
genesis 585 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
genesis 586
genesis 587 if (fInvalid)
genesis 588 throw JSONRPCError(-5, "Malformed base64 encoding");
genesis 589
genesis 590 CDataStream ss(SER_GETHASH);
genesis 591 ss << strMessageMagic;
genesis 592 ss << strMessage;
genesis 593
genesis 594 CKey key;
genesis 595 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
genesis 596 return false;
genesis 597
genesis 598 return (key.GetAddress() == addr);
genesis 599 }
genesis 600
genesis 601
genesis 602 Value getreceivedbyaddress(const Array& params, bool fHelp)
genesis 603 {
genesis 604 if (fHelp || params.size() < 1 || params.size() > 2)
genesis 605 throw runtime_error(
genesis 606 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
genesis 607 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
genesis 608
genesis 609 // Bitcoin address
genesis 610 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
genesis 611 CScript scriptPubKey;
genesis 612 if (!address.IsValid())
genesis 613 throw JSONRPCError(-5, "Invalid bitcoin address");
genesis 614 scriptPubKey.SetBitcoinAddress(address);
genesis 615 if (!IsMine(*pwalletMain,scriptPubKey))
genesis 616 return (double)0.0;
genesis 617
genesis 618 // Minimum confirmations
genesis 619 int nMinDepth = 1;
genesis 620 if (params.size() > 1)
genesis 621 nMinDepth = params[1].get_int();
genesis 622
genesis 623 // Tally
genesis 624 int64 nAmount = 0;
genesis 625 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 626 {
genesis 627 const CWalletTx& wtx = (*it).second;
genesis 628 if (wtx.IsCoinBase() || !wtx.IsFinal())
genesis 629 continue;
genesis 630
genesis 631 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
genesis 632 if (txout.scriptPubKey == scriptPubKey)
genesis 633 if (wtx.GetDepthInMainChain() >= nMinDepth)
genesis 634 nAmount += txout.nValue;
genesis 635 }
genesis 636
genesis 637 return ValueFromAmount(nAmount);
genesis 638 }
genesis 639
genesis 640
genesis 641 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
genesis 642 {
genesis 643 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
genesis 644 {
genesis 645 const CBitcoinAddress& address = item.first;
genesis 646 const string& strName = item.second;
genesis 647 if (strName == strAccount)
genesis 648 setAddress.insert(address);
genesis 649 }
genesis 650 }
genesis 651
genesis 652
genesis 653 Value getreceivedbyaccount(const Array& params, bool fHelp)
genesis 654 {
genesis 655 if (fHelp || params.size() < 1 || params.size() > 2)
genesis 656 throw runtime_error(
genesis 657 "getreceivedbyaccount <account> [minconf=1]\n"
genesis 658 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
genesis 659
genesis 660 // Minimum confirmations
genesis 661 int nMinDepth = 1;
genesis 662 if (params.size() > 1)
genesis 663 nMinDepth = params[1].get_int();
genesis 664
genesis 665 // Get the set of pub keys that have the label
genesis 666 string strAccount = AccountFromValue(params[0]);
genesis 667 set<CBitcoinAddress> setAddress;
genesis 668 GetAccountAddresses(strAccount, setAddress);
genesis 669
genesis 670 // Tally
genesis 671 int64 nAmount = 0;
genesis 672 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 673 {
genesis 674 const CWalletTx& wtx = (*it).second;
genesis 675 if (wtx.IsCoinBase() || !wtx.IsFinal())
genesis 676 continue;
genesis 677
genesis 678 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
genesis 679 {
genesis 680 CBitcoinAddress address;
genesis 681 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
genesis 682 if (wtx.GetDepthInMainChain() >= nMinDepth)
genesis 683 nAmount += txout.nValue;
genesis 684 }
genesis 685 }
genesis 686
genesis 687 return (double)nAmount / (double)COIN;
genesis 688 }
genesis 689
genesis 690
genesis 691 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
genesis 692 {
genesis 693 int64 nBalance = 0;
genesis 694
genesis 695 // Tally wallet transactions
genesis 696 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 697 {
genesis 698 const CWalletTx& wtx = (*it).second;
genesis 699 if (!wtx.IsFinal())
genesis 700 continue;
genesis 701
genesis 702 int64 nGenerated, nReceived, nSent, nFee;
genesis 703 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
genesis 704
genesis 705 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
genesis 706 nBalance += nReceived;
genesis 707 nBalance += nGenerated - nSent - nFee;
genesis 708 }
genesis 709
genesis 710 // Tally internal accounting entries
genesis 711 nBalance += walletdb.GetAccountCreditDebit(strAccount);
genesis 712
genesis 713 return nBalance;
genesis 714 }
genesis 715
genesis 716 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
genesis 717 {
genesis 718 CWalletDB walletdb(pwalletMain->strWalletFile);
genesis 719 return GetAccountBalance(walletdb, strAccount, nMinDepth);
genesis 720 }
genesis 721
genesis 722
genesis 723 Value getbalance(const Array& params, bool fHelp)
genesis 724 {
genesis 725 if (fHelp || params.size() > 2)
genesis 726 throw runtime_error(
genesis 727 "getbalance [account] [minconf=1]\n"
genesis 728 "If [account] is not specified, returns the server's total available balance.\n"
genesis 729 "If [account] is specified, returns the balance in the account.");
genesis 730
genesis 731 if (params.size() == 0)
genesis 732 return ValueFromAmount(pwalletMain->GetBalance());
genesis 733
genesis 734 int nMinDepth = 1;
genesis 735 if (params.size() > 1)
genesis 736 nMinDepth = params[1].get_int();
genesis 737
genesis 738 if (params[0].get_str() == "*") {
genesis 739 // Calculate total balance a different way from GetBalance()
genesis 740 // (GetBalance() sums up all unspent TxOuts)
genesis 741 // getbalance and getbalance '*' should always return the same number.
genesis 742 int64 nBalance = 0;
genesis 743 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 744 {
genesis 745 const CWalletTx& wtx = (*it).second;
genesis 746 if (!wtx.IsFinal())
genesis 747 continue;
genesis 748
genesis 749 int64 allGeneratedImmature, allGeneratedMature, allFee;
genesis 750 allGeneratedImmature = allGeneratedMature = allFee = 0;
genesis 751 string strSentAccount;
genesis 752 list<pair<CBitcoinAddress, int64> > listReceived;
genesis 753 list<pair<CBitcoinAddress, int64> > listSent;
genesis 754 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
genesis 755 if (wtx.GetDepthInMainChain() >= nMinDepth)
genesis 756 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
genesis 757 nBalance += r.second;
genesis 758 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
genesis 759 nBalance -= r.second;
genesis 760 nBalance -= allFee;
genesis 761 nBalance += allGeneratedMature;
genesis 762 }
genesis 763 return ValueFromAmount(nBalance);
genesis 764 }
genesis 765
genesis 766 string strAccount = AccountFromValue(params[0]);
genesis 767
genesis 768 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
genesis 769
genesis 770 return ValueFromAmount(nBalance);
genesis 771 }
genesis 772
genesis 773
genesis 774 Value movecmd(const Array& params, bool fHelp)
genesis 775 {
genesis 776 if (fHelp || params.size() < 3 || params.size() > 5)
genesis 777 throw runtime_error(
genesis 778 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
genesis 779 "Move from one account in your wallet to another.");
genesis 780
genesis 781 string strFrom = AccountFromValue(params[0]);
genesis 782 string strTo = AccountFromValue(params[1]);
genesis 783 int64 nAmount = AmountFromValue(params[2]);
genesis 784 if (params.size() > 3)
genesis 785 // unused parameter, used to be nMinDepth, keep type-checking it though
genesis 786 (void)params[3].get_int();
genesis 787 string strComment;
genesis 788 if (params.size() > 4)
genesis 789 strComment = params[4].get_str();
genesis 790
genesis 791 CWalletDB walletdb(pwalletMain->strWalletFile);
genesis 792 walletdb.TxnBegin();
genesis 793
genesis 794 int64 nNow = GetAdjustedTime();
genesis 795
genesis 796 // Debit
genesis 797 CAccountingEntry debit;
genesis 798 debit.strAccount = strFrom;
genesis 799 debit.nCreditDebit = -nAmount;
genesis 800 debit.nTime = nNow;
genesis 801 debit.strOtherAccount = strTo;
genesis 802 debit.strComment = strComment;
genesis 803 walletdb.WriteAccountingEntry(debit);
genesis 804
genesis 805 // Credit
genesis 806 CAccountingEntry credit;
genesis 807 credit.strAccount = strTo;
genesis 808 credit.nCreditDebit = nAmount;
genesis 809 credit.nTime = nNow;
genesis 810 credit.strOtherAccount = strFrom;
genesis 811 credit.strComment = strComment;
genesis 812 walletdb.WriteAccountingEntry(credit);
genesis 813
genesis 814 walletdb.TxnCommit();
genesis 815
genesis 816 return true;
genesis 817 }
genesis 818
genesis 819
genesis 820 Value sendfrom(const Array& params, bool fHelp)
genesis 821 {
genesis 822 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
genesis 823 throw runtime_error(
genesis 824 "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
genesis 825 "<amount> is a real and is rounded to the nearest 0.00000001\n"
genesis 826 "requires wallet passphrase to be set with walletpassphrase first");
genesis 827 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
genesis 828 throw runtime_error(
genesis 829 "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
genesis 830 "<amount> is a real and is rounded to the nearest 0.00000001");
genesis 831
genesis 832 string strAccount = AccountFromValue(params[0]);
genesis 833 CBitcoinAddress address(params[1].get_str());
genesis 834 if (!address.IsValid())
genesis 835 throw JSONRPCError(-5, "Invalid bitcoin address");
genesis 836 int64 nAmount = AmountFromValue(params[2]);
genesis 837 int nMinDepth = 1;
genesis 838 if (params.size() > 3)
genesis 839 nMinDepth = params[3].get_int();
genesis 840
genesis 841 CWalletTx wtx;
genesis 842 wtx.strFromAccount = strAccount;
genesis 843 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
genesis 844 wtx.mapValue["comment"] = params[4].get_str();
genesis 845 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
genesis 846 wtx.mapValue["to"] = params[5].get_str();
genesis 847
genesis 848 if (pwalletMain->IsLocked())
genesis 849 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
genesis 850
genesis 851 // Check funds
genesis 852 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
genesis 853 if (nAmount > nBalance)
genesis 854 throw JSONRPCError(-6, "Account has insufficient funds");
genesis 855
genesis 856 // Send
genesis 857 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
genesis 858 if (strError != "")
genesis 859 throw JSONRPCError(-4, strError);
genesis 860
genesis 861 return wtx.GetHash().GetHex();
genesis 862 }
genesis 863
genesis 864
genesis 865 Value sendmany(const Array& params, bool fHelp)
genesis 866 {
genesis 867 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
genesis 868 throw runtime_error(
genesis 869 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
genesis 870 "amounts are double-precision floating point numbers\n"
genesis 871 "requires wallet passphrase to be set with walletpassphrase first");
genesis 872 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
genesis 873 throw runtime_error(
genesis 874 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
genesis 875 "amounts are double-precision floating point numbers");
genesis 876
genesis 877 string strAccount = AccountFromValue(params[0]);
genesis 878 Object sendTo = params[1].get_obj();
genesis 879 int nMinDepth = 1;
genesis 880 if (params.size() > 2)
genesis 881 nMinDepth = params[2].get_int();
genesis 882
genesis 883 CWalletTx wtx;
genesis 884 wtx.strFromAccount = strAccount;
genesis 885 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
genesis 886 wtx.mapValue["comment"] = params[3].get_str();
genesis 887
genesis 888 set<CBitcoinAddress> setAddress;
genesis 889 vector<pair<CScript, int64> > vecSend;
genesis 890
genesis 891 int64 totalAmount = 0;
genesis 892 BOOST_FOREACH(const Pair& s, sendTo)
genesis 893 {
genesis 894 CBitcoinAddress address(s.name_);
genesis 895 if (!address.IsValid())
genesis 896 throw JSONRPCError(-5, string("Invalid bitcoin address:")+s.name_);
genesis 897
genesis 898 if (setAddress.count(address))
genesis 899 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
genesis 900 setAddress.insert(address);
genesis 901
genesis 902 CScript scriptPubKey;
genesis 903 scriptPubKey.SetBitcoinAddress(address);
genesis 904 int64 nAmount = AmountFromValue(s.value_);
genesis 905 totalAmount += nAmount;
genesis 906
genesis 907 vecSend.push_back(make_pair(scriptPubKey, nAmount));
genesis 908 }
genesis 909
genesis 910 if (pwalletMain->IsLocked())
genesis 911 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
genesis 912
genesis 913 // Check funds
genesis 914 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
genesis 915 if (totalAmount > nBalance)
genesis 916 throw JSONRPCError(-6, "Account has insufficient funds");
genesis 917
genesis 918 // Send
genesis 919 CReserveKey keyChange(pwalletMain);
genesis 920 int64 nFeeRequired = 0;
genesis 921 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
genesis 922 if (!fCreated)
genesis 923 {
genesis 924 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
genesis 925 throw JSONRPCError(-6, "Insufficient funds");
genesis 926 throw JSONRPCError(-4, "Transaction creation failed");
genesis 927 }
genesis 928 if (!pwalletMain->CommitTransaction(wtx, keyChange))
genesis 929 throw JSONRPCError(-4, "Transaction commit failed");
genesis 930
genesis 931 return wtx.GetHash().GetHex();
genesis 932 }
genesis 933
genesis 934
genesis 935 struct tallyitem
genesis 936 {
genesis 937 int64 nAmount;
genesis 938 int nConf;
genesis 939 tallyitem()
genesis 940 {
genesis 941 nAmount = 0;
genesis 942 nConf = INT_MAX;
genesis 943 }
genesis 944 };
genesis 945
genesis 946 Value ListReceived(const Array& params, bool fByAccounts)
genesis 947 {
genesis 948 // Minimum confirmations
genesis 949 int nMinDepth = 1;
genesis 950 if (params.size() > 0)
genesis 951 nMinDepth = params[0].get_int();
genesis 952
genesis 953 // Whether to include empty accounts
genesis 954 bool fIncludeEmpty = false;
genesis 955 if (params.size() > 1)
genesis 956 fIncludeEmpty = params[1].get_bool();
genesis 957
genesis 958 // Tally
genesis 959 map<CBitcoinAddress, tallyitem> mapTally;
genesis 960 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 961 {
genesis 962 const CWalletTx& wtx = (*it).second;
genesis 963 if (wtx.IsCoinBase() || !wtx.IsFinal())
genesis 964 continue;
genesis 965
genesis 966 int nDepth = wtx.GetDepthInMainChain();
genesis 967 if (nDepth < nMinDepth)
genesis 968 continue;
genesis 969
genesis 970 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
genesis 971 {
genesis 972 CBitcoinAddress address;
genesis 973 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
genesis 974 continue;
genesis 975
genesis 976 tallyitem& item = mapTally[address];
genesis 977 item.nAmount += txout.nValue;
genesis 978 item.nConf = min(item.nConf, nDepth);
genesis 979 }
genesis 980 }
genesis 981
genesis 982 // Reply
genesis 983 Array ret;
genesis 984 map<string, tallyitem> mapAccountTally;
genesis 985 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
genesis 986 {
genesis 987 const CBitcoinAddress& address = item.first;
genesis 988 const string& strAccount = item.second;
genesis 989 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
genesis 990 if (it == mapTally.end() && !fIncludeEmpty)
genesis 991 continue;
genesis 992
genesis 993 int64 nAmount = 0;
genesis 994 int nConf = INT_MAX;
genesis 995 if (it != mapTally.end())
genesis 996 {
genesis 997 nAmount = (*it).second.nAmount;
genesis 998 nConf = (*it).second.nConf;
genesis 999 }
genesis 1000
genesis 1001 if (fByAccounts)
genesis 1002 {
genesis 1003 tallyitem& item = mapAccountTally[strAccount];
genesis 1004 item.nAmount += nAmount;
genesis 1005 item.nConf = min(item.nConf, nConf);
genesis 1006 }
genesis 1007 else
genesis 1008 {
genesis 1009 Object obj;
genesis 1010 obj.push_back(Pair("address", address.ToString()));
genesis 1011 obj.push_back(Pair("account", strAccount));
genesis 1012 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
genesis 1013 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
genesis 1014 ret.push_back(obj);
genesis 1015 }
genesis 1016 }
genesis 1017
genesis 1018 if (fByAccounts)
genesis 1019 {
genesis 1020 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
genesis 1021 {
genesis 1022 int64 nAmount = (*it).second.nAmount;
genesis 1023 int nConf = (*it).second.nConf;
genesis 1024 Object obj;
genesis 1025 obj.push_back(Pair("account", (*it).first));
genesis 1026 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
genesis 1027 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
genesis 1028 ret.push_back(obj);
genesis 1029 }
genesis 1030 }
genesis 1031
genesis 1032 return ret;
genesis 1033 }
genesis 1034
genesis 1035 Value listreceivedbyaddress(const Array& params, bool fHelp)
genesis 1036 {
genesis 1037 if (fHelp || params.size() > 2)
genesis 1038 throw runtime_error(
genesis 1039 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
genesis 1040 "[minconf] is the minimum number of confirmations before payments are included.\n"
genesis 1041 "[includeempty] whether to include addresses that haven't received any payments.\n"
genesis 1042 "Returns an array of objects containing:\n"
genesis 1043 " \"address\" : receiving address\n"
genesis 1044 " \"account\" : the account of the receiving address\n"
genesis 1045 " \"amount\" : total amount received by the address\n"
genesis 1046 " \"confirmations\" : number of confirmations of the most recent transaction included");
genesis 1047
genesis 1048 return ListReceived(params, false);
genesis 1049 }
genesis 1050
genesis 1051 Value listreceivedbyaccount(const Array& params, bool fHelp)
genesis 1052 {
genesis 1053 if (fHelp || params.size() > 2)
genesis 1054 throw runtime_error(
genesis 1055 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
genesis 1056 "[minconf] is the minimum number of confirmations before payments are included.\n"
genesis 1057 "[includeempty] whether to include accounts that haven't received any payments.\n"
genesis 1058 "Returns an array of objects containing:\n"
genesis 1059 " \"account\" : the account of the receiving addresses\n"
genesis 1060 " \"amount\" : total amount received by addresses with this account\n"
genesis 1061 " \"confirmations\" : number of confirmations of the most recent transaction included");
genesis 1062
genesis 1063 return ListReceived(params, true);
genesis 1064 }
genesis 1065
genesis 1066 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
genesis 1067 {
genesis 1068 int64 nGeneratedImmature, nGeneratedMature, nFee;
genesis 1069 string strSentAccount;
genesis 1070 list<pair<CBitcoinAddress, int64> > listReceived;
genesis 1071 list<pair<CBitcoinAddress, int64> > listSent;
genesis 1072 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
genesis 1073
genesis 1074 bool fAllAccounts = (strAccount == string("*"));
genesis 1075
genesis 1076 // Generated blocks assigned to account ""
genesis 1077 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
genesis 1078 {
genesis 1079 Object entry;
genesis 1080 entry.push_back(Pair("account", string("")));
genesis 1081 if (nGeneratedImmature)
genesis 1082 {
genesis 1083 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
genesis 1084 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
genesis 1085 }
genesis 1086 else
genesis 1087 {
genesis 1088 entry.push_back(Pair("category", "generate"));
genesis 1089 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
genesis 1090 }
genesis 1091 if (fLong)
genesis 1092 WalletTxToJSON(wtx, entry);
genesis 1093 ret.push_back(entry);
genesis 1094 }
genesis 1095
genesis 1096 // Sent
genesis 1097 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
genesis 1098 {
genesis 1099 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
genesis 1100 {
genesis 1101 Object entry;
genesis 1102 entry.push_back(Pair("account", strSentAccount));
genesis 1103 entry.push_back(Pair("address", s.first.ToString()));
genesis 1104 entry.push_back(Pair("category", "send"));
genesis 1105 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
genesis 1106 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
genesis 1107 if (fLong)
genesis 1108 WalletTxToJSON(wtx, entry);
genesis 1109 ret.push_back(entry);
genesis 1110 }
genesis 1111 }
genesis 1112
genesis 1113 // Received
genesis 1114 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
genesis 1115 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
genesis 1116 {
genesis 1117 string account;
genesis 1118 if (pwalletMain->mapAddressBook.count(r.first))
genesis 1119 account = pwalletMain->mapAddressBook[r.first];
genesis 1120 if (fAllAccounts || (account == strAccount))
genesis 1121 {
genesis 1122 Object entry;
genesis 1123 entry.push_back(Pair("account", account));
genesis 1124 entry.push_back(Pair("address", r.first.ToString()));
genesis 1125 entry.push_back(Pair("category", "receive"));
genesis 1126 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
genesis 1127 if (fLong)
genesis 1128 WalletTxToJSON(wtx, entry);
genesis 1129 ret.push_back(entry);
genesis 1130 }
genesis 1131 }
genesis 1132 }
genesis 1133
genesis 1134 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
genesis 1135 {
genesis 1136 bool fAllAccounts = (strAccount == string("*"));
genesis 1137
genesis 1138 if (fAllAccounts || acentry.strAccount == strAccount)
genesis 1139 {
genesis 1140 Object entry;
genesis 1141 entry.push_back(Pair("account", acentry.strAccount));
genesis 1142 entry.push_back(Pair("category", "move"));
genesis 1143 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
genesis 1144 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
genesis 1145 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
genesis 1146 entry.push_back(Pair("comment", acentry.strComment));
genesis 1147 ret.push_back(entry);
genesis 1148 }
genesis 1149 }
genesis 1150
genesis 1151 Value listtransactions(const Array& params, bool fHelp)
genesis 1152 {
genesis 1153 if (fHelp || params.size() > 3)
genesis 1154 throw runtime_error(
genesis 1155 "listtransactions [account] [count=10] [from=0]\n"
genesis 1156 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
genesis 1157
genesis 1158 string strAccount = "*";
genesis 1159 if (params.size() > 0)
genesis 1160 strAccount = params[0].get_str();
genesis 1161 int nCount = 10;
genesis 1162 if (params.size() > 1)
genesis 1163 nCount = params[1].get_int();
genesis 1164 int nFrom = 0;
genesis 1165 if (params.size() > 2)
genesis 1166 nFrom = params[2].get_int();
genesis 1167
genesis 1168 Array ret;
genesis 1169 CWalletDB walletdb(pwalletMain->strWalletFile);
genesis 1170
genesis 1171 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
genesis 1172 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
genesis 1173 typedef multimap<int64, TxPair > TxItems;
genesis 1174 TxItems txByTime;
genesis 1175
genesis 1176 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 1177 {
genesis 1178 CWalletTx* wtx = &((*it).second);
genesis 1179 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
genesis 1180 }
genesis 1181 list<CAccountingEntry> acentries;
genesis 1182 walletdb.ListAccountCreditDebit(strAccount, acentries);
genesis 1183 BOOST_FOREACH(CAccountingEntry& entry, acentries)
genesis 1184 {
genesis 1185 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
genesis 1186 }
genesis 1187
genesis 1188 // Now: iterate backwards until we have nCount items to return:
genesis 1189 TxItems::reverse_iterator it = txByTime.rbegin();
genesis 1190 if (txByTime.size() > nFrom) std::advance(it, nFrom);
genesis 1191 for (; it != txByTime.rend(); ++it)
genesis 1192 {
genesis 1193 CWalletTx *const pwtx = (*it).second.first;
genesis 1194 if (pwtx != 0)
genesis 1195 ListTransactions(*pwtx, strAccount, 0, true, ret);
genesis 1196 CAccountingEntry *const pacentry = (*it).second.second;
genesis 1197 if (pacentry != 0)
genesis 1198 AcentryToJSON(*pacentry, strAccount, ret);
genesis 1199
genesis 1200 if (ret.size() >= nCount) break;
genesis 1201 }
genesis 1202 // ret is now newest to oldest
genesis 1203
genesis 1204 // Make sure we return only last nCount items (sends-to-self might give us an extra):
genesis 1205 if (ret.size() > nCount)
genesis 1206 {
genesis 1207 Array::iterator last = ret.begin();
genesis 1208 std::advance(last, nCount);
genesis 1209 ret.erase(last, ret.end());
genesis 1210 }
genesis 1211 std::reverse(ret.begin(), ret.end()); // oldest to newest
genesis 1212
genesis 1213 return ret;
genesis 1214 }
genesis 1215
genesis 1216 Value listaccounts(const Array& params, bool fHelp)
genesis 1217 {
genesis 1218 if (fHelp || params.size() > 1)
genesis 1219 throw runtime_error(
genesis 1220 "listaccounts [minconf=1]\n"
genesis 1221 "Returns Object that has account names as keys, account balances as values.");
genesis 1222
genesis 1223 int nMinDepth = 1;
genesis 1224 if (params.size() > 0)
genesis 1225 nMinDepth = params[0].get_int();
genesis 1226
genesis 1227 map<string, int64> mapAccountBalances;
genesis 1228 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
genesis 1229 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
genesis 1230 mapAccountBalances[entry.second] = 0;
genesis 1231 }
genesis 1232
genesis 1233 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
genesis 1234 {
genesis 1235 const CWalletTx& wtx = (*it).second;
genesis 1236 int64 nGeneratedImmature, nGeneratedMature, nFee;
genesis 1237 string strSentAccount;
genesis 1238 list<pair<CBitcoinAddress, int64> > listReceived;
genesis 1239 list<pair<CBitcoinAddress, int64> > listSent;
genesis 1240 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
genesis 1241 mapAccountBalances[strSentAccount] -= nFee;
genesis 1242 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
genesis 1243 mapAccountBalances[strSentAccount] -= s.second;
genesis 1244 if (wtx.GetDepthInMainChain() >= nMinDepth)
genesis 1245 {
genesis 1246 mapAccountBalances[""] += nGeneratedMature;
genesis 1247 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
genesis 1248 if (pwalletMain->mapAddressBook.count(r.first))
genesis 1249 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
genesis 1250 else
genesis 1251 mapAccountBalances[""] += r.second;
genesis 1252 }
genesis 1253 }
genesis 1254
genesis 1255 list<CAccountingEntry> acentries;
genesis 1256 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
genesis 1257 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
genesis 1258 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
genesis 1259
genesis 1260 Object ret;
genesis 1261 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
genesis 1262 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
genesis 1263 }
genesis 1264 return ret;
genesis 1265 }
genesis 1266
genesis 1267 Value listsinceblock(const Array& params, bool fHelp)
genesis 1268 {
genesis 1269 if (fHelp)
genesis 1270 throw runtime_error(
genesis 1271 "listsinceblock [blockid] [target-confirmations]\n"
genesis 1272 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
genesis 1273
genesis 1274 CBlockIndex *pindex = NULL;
genesis 1275 int target_confirms = 1;
genesis 1276
genesis 1277 if (params.size() > 0)
genesis 1278 {
genesis 1279 uint256 blockId = 0;
genesis 1280
genesis 1281 blockId.SetHex(params[0].get_str());
genesis 1282 pindex = CBlockLocator(blockId).GetBlockIndex();
genesis 1283 }
genesis 1284
genesis 1285 if (params.size() > 1)
genesis 1286 {
genesis 1287 target_confirms = params[1].get_int();
genesis 1288
genesis 1289 if (target_confirms < 1)
genesis 1290 throw JSONRPCError(-8, "Invalid parameter");
genesis 1291 }
genesis 1292
genesis 1293 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
genesis 1294
genesis 1295 Array transactions;
genesis 1296
genesis 1297 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
genesis 1298 {
genesis 1299 CWalletTx tx = (*it).second;
genesis 1300
genesis 1301 if (depth == -1 || tx.GetDepthInMainChain() < depth)
genesis 1302 ListTransactions(tx, "*", 0, true, transactions);
genesis 1303 }
genesis 1304
genesis 1305 uint256 lastblock;
genesis 1306
genesis 1307 if (target_confirms == 1)
genesis 1308 {
genesis 1309 printf("oops!\n");
genesis 1310 lastblock = hashBestChain;
genesis 1311 }
genesis 1312 else
genesis 1313 {
genesis 1314 int target_height = pindexBest->nHeight + 1 - target_confirms;
genesis 1315
genesis 1316 CBlockIndex *block;
genesis 1317 for (block = pindexBest;
genesis 1318 block && block->nHeight > target_height;
genesis 1319 block = block->pprev) { }
genesis 1320
genesis 1321 lastblock = block ? block->GetBlockHash() : 0;
genesis 1322 }
genesis 1323
genesis 1324 Object ret;
genesis 1325 ret.push_back(Pair("transactions", transactions));
genesis 1326 ret.push_back(Pair("lastblock", lastblock.GetHex()));
genesis 1327
genesis 1328 return ret;
genesis 1329 }
genesis 1330
genesis 1331 Value gettransaction(const Array& params, bool fHelp)
genesis 1332 {
genesis 1333 if (fHelp || params.size() != 1)
genesis 1334 throw runtime_error(
genesis 1335 "gettransaction <txid>\n"
genesis 1336 "Get detailed information about <txid>");
genesis 1337
genesis 1338 uint256 hash;
genesis 1339 hash.SetHex(params[0].get_str());
genesis 1340
genesis 1341 Object entry;
genesis 1342
genesis 1343 if (!pwalletMain->mapWallet.count(hash))
genesis 1344 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
genesis 1345 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
genesis 1346
genesis 1347 int64 nCredit = wtx.GetCredit();
genesis 1348 int64 nDebit = wtx.GetDebit();
genesis 1349 int64 nNet = nCredit - nDebit;
genesis 1350 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
genesis 1351
genesis 1352 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
genesis 1353 if (wtx.IsFromMe())
genesis 1354 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
genesis 1355
genesis 1356 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
genesis 1357
genesis 1358 Array details;
genesis 1359 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
genesis 1360 entry.push_back(Pair("details", details));
genesis 1361
genesis 1362 return entry;
genesis 1363 }
genesis 1364
genesis 1365
genesis 1366 Value backupwallet(const Array& params, bool fHelp)
genesis 1367 {
genesis 1368 if (fHelp || params.size() != 1)
genesis 1369 throw runtime_error(
genesis 1370 "backupwallet <destination>\n"
genesis 1371 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
genesis 1372
genesis 1373 string strDest = params[0].get_str();
genesis 1374 BackupWallet(*pwalletMain, strDest);
genesis 1375
genesis 1376 return Value::null;
genesis 1377 }
genesis 1378
genesis 1379
genesis 1380 Value keypoolrefill(const Array& params, bool fHelp)
genesis 1381 {
genesis 1382 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
genesis 1383 throw runtime_error(
genesis 1384 "keypoolrefill\n"
genesis 1385 "Fills the keypool, requires wallet passphrase to be set.");
genesis 1386 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
genesis 1387 throw runtime_error(
genesis 1388 "keypoolrefill\n"
genesis 1389 "Fills the keypool.");
genesis 1390
genesis 1391 if (pwalletMain->IsLocked())
genesis 1392 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
genesis 1393
genesis 1394 pwalletMain->TopUpKeyPool();
genesis 1395
genesis 1396 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
genesis 1397 throw JSONRPCError(-4, "Error refreshing keypool.");
genesis 1398
genesis 1399 return Value::null;
genesis 1400 }
genesis 1401
genesis 1402
genesis 1403 void ThreadTopUpKeyPool(void* parg)
genesis 1404 {
genesis 1405 pwalletMain->TopUpKeyPool();
genesis 1406 }
genesis 1407
genesis 1408 void ThreadCleanWalletPassphrase(void* parg)
genesis 1409 {
genesis 1410 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
genesis 1411
genesis 1412 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
genesis 1413
genesis 1414 if (nWalletUnlockTime == 0)
genesis 1415 {
genesis 1416 nWalletUnlockTime = nMyWakeTime;
genesis 1417
genesis 1418 do
genesis 1419 {
genesis 1420 if (nWalletUnlockTime==0)
genesis 1421 break;
genesis 1422 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
genesis 1423 if (nToSleep <= 0)
genesis 1424 break;
genesis 1425
genesis 1426 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
genesis 1427 Sleep(nToSleep);
genesis 1428 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
genesis 1429
genesis 1430 } while(1);
genesis 1431
genesis 1432 if (nWalletUnlockTime)
genesis 1433 {
genesis 1434 nWalletUnlockTime = 0;
genesis 1435 pwalletMain->Lock();
genesis 1436 }
genesis 1437 }
genesis 1438 else
genesis 1439 {
genesis 1440 if (nWalletUnlockTime < nMyWakeTime)
genesis 1441 nWalletUnlockTime = nMyWakeTime;
genesis 1442 }
genesis 1443
genesis 1444 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
genesis 1445
genesis 1446 delete (int64*)parg;
genesis 1447 }
genesis 1448
genesis 1449 Value walletpassphrase(const Array& params, bool fHelp)
genesis 1450 {
genesis 1451 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
genesis 1452 throw runtime_error(
genesis 1453 "walletpassphrase <passphrase> <timeout>\n"
genesis 1454 "Stores the wallet decryption key in memory for <timeout> seconds.");
genesis 1455 if (fHelp)
genesis 1456 return true;
genesis 1457 if (!pwalletMain->IsCrypted())
genesis 1458 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
genesis 1459
genesis 1460 if (!pwalletMain->IsLocked())
genesis 1461 throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
genesis 1462
genesis 1463 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
genesis 1464 SecureString strWalletPass;
genesis 1465 strWalletPass.reserve(100);
genesis 1466 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
genesis 1467 // Alternately, find a way to make params[0] mlock()'d to begin with.
genesis 1468 strWalletPass = params[0].get_str().c_str();
genesis 1469
genesis 1470 if (strWalletPass.length() > 0)
genesis 1471 {
genesis 1472 if (!pwalletMain->Unlock(strWalletPass))
genesis 1473 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
genesis 1474 }
genesis 1475 else
genesis 1476 throw runtime_error(
genesis 1477 "walletpassphrase <passphrase> <timeout>\n"
genesis 1478 "Stores the wallet decryption key in memory for <timeout> seconds.");
genesis 1479
genesis 1480 CreateThread(ThreadTopUpKeyPool, NULL);
genesis 1481 int64* pnSleepTime = new int64(params[1].get_int64());
genesis 1482 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
genesis 1483
genesis 1484 return Value::null;
genesis 1485 }
genesis 1486
genesis 1487
genesis 1488 Value walletpassphrasechange(const Array& params, bool fHelp)
genesis 1489 {
genesis 1490 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
genesis 1491 throw runtime_error(
genesis 1492 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
genesis 1493 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
genesis 1494 if (fHelp)
genesis 1495 return true;
genesis 1496 if (!pwalletMain->IsCrypted())
genesis 1497 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
genesis 1498
genesis 1499 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
genesis 1500 // Alternately, find a way to make params[0] mlock()'d to begin with.
genesis 1501 SecureString strOldWalletPass;
genesis 1502 strOldWalletPass.reserve(100);
genesis 1503 strOldWalletPass = params[0].get_str().c_str();
genesis 1504
genesis 1505 SecureString strNewWalletPass;
genesis 1506 strNewWalletPass.reserve(100);
genesis 1507 strNewWalletPass = params[1].get_str().c_str();
genesis 1508
genesis 1509 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
genesis 1510 throw runtime_error(
genesis 1511 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
genesis 1512 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
genesis 1513
genesis 1514 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
genesis 1515 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
genesis 1516
genesis 1517 return Value::null;
genesis 1518 }
genesis 1519
genesis 1520
genesis 1521 Value walletlock(const Array& params, bool fHelp)
genesis 1522 {
genesis 1523 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
genesis 1524 throw runtime_error(
genesis 1525 "walletlock\n"
genesis 1526 "Removes the wallet encryption key from memory, locking the wallet.\n"
genesis 1527 "After calling this method, you will need to call walletpassphrase again\n"
genesis 1528 "before being able to call any methods which require the wallet to be unlocked.");
genesis 1529 if (fHelp)
genesis 1530 return true;
genesis 1531 if (!pwalletMain->IsCrypted())
genesis 1532 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
genesis 1533
genesis 1534 CRITICAL_BLOCK(cs_nWalletUnlockTime)
genesis 1535 {
genesis 1536 pwalletMain->Lock();
genesis 1537 nWalletUnlockTime = 0;
genesis 1538 }
genesis 1539
genesis 1540 return Value::null;
genesis 1541 }
genesis 1542
genesis 1543
genesis 1544 Value encryptwallet(const Array& params, bool fHelp)
genesis 1545 {
genesis 1546 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
genesis 1547 throw runtime_error(
genesis 1548 "encryptwallet <passphrase>\n"
genesis 1549 "Encrypts the wallet with <passphrase>.");
genesis 1550 if (fHelp)
genesis 1551 return true;
genesis 1552 if (pwalletMain->IsCrypted())
genesis 1553 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
genesis 1554
genesis 1555 #ifdef QT_GUI
genesis 1556 // shutting down via RPC while the GUI is running does not work (yet):
genesis 1557 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
genesis 1558 #endif
genesis 1559
genesis 1560 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
genesis 1561 // Alternately, find a way to make params[0] mlock()'d to begin with.
genesis 1562 SecureString strWalletPass;
genesis 1563 strWalletPass.reserve(100);
genesis 1564 strWalletPass = params[0].get_str().c_str();
genesis 1565
genesis 1566 if (strWalletPass.length() < 1)
genesis 1567 throw runtime_error(
genesis 1568 "encryptwallet <passphrase>\n"
genesis 1569 "Encrypts the wallet with <passphrase>.");
genesis 1570
genesis 1571 if (!pwalletMain->EncryptWallet(strWalletPass))
genesis 1572 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
genesis 1573
genesis 1574 // BDB seems to have a bad habit of writing old data into
genesis 1575 // slack space in .dat files; that is bad if the old data is
genesis 1576 // unencrypted private keys. So:
genesis 1577 CreateThread(Shutdown, NULL);
genesis 1578 return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet";
genesis 1579 }
genesis 1580
genesis 1581
genesis 1582 Value validateaddress(const Array& params, bool fHelp)
genesis 1583 {
genesis 1584 if (fHelp || params.size() != 1)
genesis 1585 throw runtime_error(
genesis 1586 "validateaddress <bitcoinaddress>\n"
genesis 1587 "Return information about <bitcoinaddress>.");
genesis 1588
genesis 1589 CBitcoinAddress address(params[0].get_str());
genesis 1590 bool isValid = address.IsValid();
genesis 1591
genesis 1592 Object ret;
genesis 1593 ret.push_back(Pair("isvalid", isValid));
genesis 1594 if (isValid)
genesis 1595 {
genesis 1596 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
genesis 1597 // version of the address:
genesis 1598 string currentAddress = address.ToString();
genesis 1599 ret.push_back(Pair("address", currentAddress));
genesis 1600 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
genesis 1601 if (pwalletMain->mapAddressBook.count(address))
genesis 1602 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
genesis 1603 }
genesis 1604 return ret;
genesis 1605 }
genesis 1606
genesis 1607
genesis 1608 Value getwork(const Array& params, bool fHelp)
genesis 1609 {
genesis 1610 if (fHelp || params.size() > 1)
genesis 1611 throw runtime_error(
genesis 1612 "getwork [data]\n"
genesis 1613 "If [data] is not specified, returns formatted hash data to work on:\n"
genesis 1614 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
genesis 1615 " \"data\" : block data\n"
genesis 1616 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
genesis 1617 " \"target\" : little endian hash target\n"
genesis 1618 "If [data] is specified, tries to solve the block and returns true if it was successful.");
genesis 1619
genesis 1620 if (vNodes.empty())
genesis 1621 throw JSONRPCError(-9, "Bitcoin is not connected!");
genesis 1622
genesis 1623 if (IsInitialBlockDownload())
genesis 1624 throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
genesis 1625
genesis 1626 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
genesis 1627 static mapNewBlock_t mapNewBlock;
genesis 1628 static vector<CBlock*> vNewBlock;
genesis 1629 static CReserveKey reservekey(pwalletMain);
genesis 1630
genesis 1631 if (params.size() == 0)
genesis 1632 {
genesis 1633 // Update block
genesis 1634 static unsigned int nTransactionsUpdatedLast;
genesis 1635 static CBlockIndex* pindexPrev;
genesis 1636 static int64 nStart;
genesis 1637 static CBlock* pblock;
genesis 1638 if (pindexPrev != pindexBest ||
genesis 1639 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
genesis 1640 {
genesis 1641 if (pindexPrev != pindexBest)
genesis 1642 {
genesis 1643 // Deallocate old blocks since they're obsolete now
genesis 1644 mapNewBlock.clear();
genesis 1645 BOOST_FOREACH(CBlock* pblock, vNewBlock)
genesis 1646 delete pblock;
genesis 1647 vNewBlock.clear();
genesis 1648 }
genesis 1649 nTransactionsUpdatedLast = nTransactionsUpdated;
genesis 1650 pindexPrev = pindexBest;
genesis 1651 nStart = GetTime();
genesis 1652
genesis 1653 // Create new block
genesis 1654 pblock = CreateNewBlock(reservekey);
genesis 1655 if (!pblock)
genesis 1656 throw JSONRPCError(-7, "Out of memory");
genesis 1657 vNewBlock.push_back(pblock);
genesis 1658 }
genesis 1659
genesis 1660 // Update nTime
genesis 1661 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
genesis 1662 pblock->nNonce = 0;
genesis 1663
genesis 1664 // Update nExtraNonce
genesis 1665 static unsigned int nExtraNonce = 0;
genesis 1666 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
genesis 1667
genesis 1668 // Save
genesis 1669 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
genesis 1670
genesis 1671 // Prebuild hash buffers
genesis 1672 char pmidstate[32];
genesis 1673 char pdata[128];
genesis 1674 char phash1[64];
genesis 1675 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
genesis 1676
genesis 1677 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
genesis 1678
genesis 1679 Object result;
genesis 1680 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
genesis 1681 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
genesis 1682 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
genesis 1683 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
genesis 1684 return result;
genesis 1685 }
genesis 1686 else
genesis 1687 {
genesis 1688 // Parse parameters
genesis 1689 vector<unsigned char> vchData = ParseHex(params[0].get_str());
genesis 1690 if (vchData.size() != 128)
genesis 1691 throw JSONRPCError(-8, "Invalid parameter");
genesis 1692 CBlock* pdata = (CBlock*)&vchData[0];
genesis 1693
genesis 1694 // Byte reverse
genesis 1695 for (int i = 0; i < 128/4; i++)
genesis 1696 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
genesis 1697
genesis 1698 // Get saved block
genesis 1699 if (!mapNewBlock.count(pdata->hashMerkleRoot))
genesis 1700 return false;
genesis 1701 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
genesis 1702
genesis 1703 pblock->nTime = pdata->nTime;
genesis 1704 pblock->nNonce = pdata->nNonce;
genesis 1705 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
genesis 1706 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
genesis 1707
genesis 1708 return CheckWork(pblock, *pwalletMain, reservekey);
genesis 1709 }
genesis 1710 }
genesis 1711
genesis 1712
genesis 1713 Value getmemorypool(const Array& params, bool fHelp)
genesis 1714 {
genesis 1715 if (fHelp || params.size() > 1)
genesis 1716 throw runtime_error(
genesis 1717 "getmemorypool [data]\n"
genesis 1718 "If [data] is not specified, returns data needed to construct a block to work on:\n"
genesis 1719 " \"version\" : block version\n"
genesis 1720 " \"previousblockhash\" : hash of current highest block\n"
genesis 1721 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
genesis 1722 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
genesis 1723 " \"time\" : timestamp appropriate for next block\n"
genesis 1724 " \"bits\" : compressed target of next block\n"
genesis 1725 "If [data] is specified, tries to solve the block and returns true if it was successful.");
genesis 1726
genesis 1727 if (params.size() == 0)
genesis 1728 {
genesis 1729 if (vNodes.empty())
genesis 1730 throw JSONRPCError(-9, "Bitcoin is not connected!");
genesis 1731
genesis 1732 if (IsInitialBlockDownload())
genesis 1733 throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
genesis 1734
genesis 1735 static CReserveKey reservekey(pwalletMain);
genesis 1736
genesis 1737 // Update block
genesis 1738 static unsigned int nTransactionsUpdatedLast;
genesis 1739 static CBlockIndex* pindexPrev;
genesis 1740 static int64 nStart;
genesis 1741 static CBlock* pblock;
genesis 1742 if (pindexPrev != pindexBest ||
genesis 1743 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
genesis 1744 {
genesis 1745 nTransactionsUpdatedLast = nTransactionsUpdated;
genesis 1746 pindexPrev = pindexBest;
genesis 1747 nStart = GetTime();
genesis 1748
genesis 1749 // Create new block
genesis 1750 if(pblock)
genesis 1751 delete pblock;
genesis 1752 pblock = CreateNewBlock(reservekey);
genesis 1753 if (!pblock)
genesis 1754 throw JSONRPCError(-7, "Out of memory");
genesis 1755 }
genesis 1756
genesis 1757 // Update nTime
genesis 1758 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
genesis 1759 pblock->nNonce = 0;
genesis 1760
genesis 1761 Array transactions;
genesis 1762 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
genesis 1763 if(tx.IsCoinBase())
genesis 1764 continue;
genesis 1765
genesis 1766 CDataStream ssTx;
genesis 1767 ssTx << tx;
genesis 1768
genesis 1769 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
genesis 1770 }
genesis 1771
genesis 1772 Object result;
genesis 1773 result.push_back(Pair("version", pblock->nVersion));
genesis 1774 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
genesis 1775 result.push_back(Pair("transactions", transactions));
genesis 1776 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
genesis 1777 result.push_back(Pair("time", (int64_t)pblock->nTime));
genesis 1778
genesis 1779 union {
genesis 1780 int32_t nBits;
genesis 1781 char cBits[4];
genesis 1782 } uBits;
genesis 1783 uBits.nBits = htonl((int32_t)pblock->nBits);
genesis 1784 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
genesis 1785
genesis 1786 return result;
genesis 1787 }
genesis 1788 else
genesis 1789 {
genesis 1790 // Parse parameters
genesis 1791 CDataStream ssBlock(ParseHex(params[0].get_str()));
genesis 1792 CBlock pblock;
genesis 1793 ssBlock >> pblock;
genesis 1794
genesis 1795 return ProcessBlock(NULL, &pblock);
genesis 1796 }
genesis 1797 }
genesis 1798
genesis 1799
genesis 1800
genesis 1801
genesis 1802
genesis 1803
genesis 1804
genesis 1805
genesis 1806
genesis 1807
genesis 1808
genesis 1809 //
genesis 1810 // Call Table
genesis 1811 //
genesis 1812
genesis 1813 pair<string, rpcfn_type> pCallTable[] =
genesis 1814 {
genesis 1815 make_pair("help", &help),
genesis 1816 make_pair("stop", &stop),
genesis 1817 make_pair("getblockcount", &getblockcount),
genesis 1818 make_pair("getblocknumber", &getblocknumber),
genesis 1819 make_pair("getconnectioncount", &getconnectioncount),
genesis 1820 make_pair("getdifficulty", &getdifficulty),
genesis 1821 make_pair("getgenerate", &getgenerate),
genesis 1822 make_pair("setgenerate", &setgenerate),
genesis 1823 make_pair("gethashespersec", &gethashespersec),
genesis 1824 make_pair("getinfo", &getinfo),
genesis 1825 make_pair("getnewaddress", &getnewaddress),
genesis 1826 make_pair("getaccountaddress", &getaccountaddress),
genesis 1827 make_pair("setaccount", &setaccount),
genesis 1828 make_pair("getaccount", &getaccount),
genesis 1829 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
genesis 1830 make_pair("sendtoaddress", &sendtoaddress),
genesis 1831 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
genesis 1832 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
genesis 1833 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
genesis 1834 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
genesis 1835 make_pair("backupwallet", &backupwallet),
genesis 1836 make_pair("keypoolrefill", &keypoolrefill),
genesis 1837 make_pair("walletpassphrase", &walletpassphrase),
genesis 1838 make_pair("walletpassphrasechange", &walletpassphrasechange),
genesis 1839 make_pair("walletlock", &walletlock),
genesis 1840 make_pair("encryptwallet", &encryptwallet),
genesis 1841 make_pair("validateaddress", &validateaddress),
genesis 1842 make_pair("getbalance", &getbalance),
genesis 1843 make_pair("move", &movecmd),
genesis 1844 make_pair("sendfrom", &sendfrom),
genesis 1845 make_pair("sendmany", &sendmany),
genesis 1846 make_pair("gettransaction", &gettransaction),
genesis 1847 make_pair("listtransactions", &listtransactions),
genesis 1848 make_pair("signmessage", &signmessage),
genesis 1849 make_pair("verifymessage", &verifymessage),
genesis 1850 make_pair("getwork", &getwork),
genesis 1851 make_pair("listaccounts", &listaccounts),
genesis 1852 make_pair("settxfee", &settxfee),
genesis 1853 make_pair("getmemorypool", &getmemorypool),
genesis 1854 make_pair("listsinceblock", &listsinceblock),
genesis 1855 };
genesis 1856 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
genesis 1857
genesis 1858 string pAllowInSafeMode[] =
genesis 1859 {
genesis 1860 "help",
genesis 1861 "stop",
genesis 1862 "getblockcount",
genesis 1863 "getblocknumber", // deprecated
genesis 1864 "getconnectioncount",
genesis 1865 "getdifficulty",
genesis 1866 "getgenerate",
genesis 1867 "setgenerate",
genesis 1868 "gethashespersec",
genesis 1869 "getinfo",
genesis 1870 "getnewaddress",
genesis 1871 "getaccountaddress",
genesis 1872 "getaccount",
genesis 1873 "getaddressesbyaccount",
genesis 1874 "backupwallet",
genesis 1875 "keypoolrefill",
genesis 1876 "walletpassphrase",
genesis 1877 "walletlock",
genesis 1878 "validateaddress",
genesis 1879 "getwork",
genesis 1880 "getmemorypool",
genesis 1881 };
genesis 1882 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
genesis 1883
genesis 1884
genesis 1885
genesis 1886
genesis 1887 //
genesis 1888 // HTTP protocol
genesis 1889 //
genesis 1890 // This ain't Apache. We're just using HTTP header for the length field
genesis 1891 // and to be compatible with other JSON-RPC implementations.
genesis 1892 //
genesis 1893
genesis 1894 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
genesis 1895 {
genesis 1896 ostringstream s;
genesis 1897 s << "POST / HTTP/1.1\r\n"
genesis 1898 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
genesis 1899 << "Host: 127.0.0.1\r\n"
genesis 1900 << "Content-Type: application/json\r\n"
genesis 1901 << "Content-Length: " << strMsg.size() << "\r\n"
genesis 1902 << "Connection: close\r\n"
genesis 1903 << "Accept: application/json\r\n";
genesis 1904 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
genesis 1905 s << item.first << ": " << item.second << "\r\n";
genesis 1906 s << "\r\n" << strMsg;
genesis 1907
genesis 1908 return s.str();
genesis 1909 }
genesis 1910
genesis 1911 string rfc1123Time()
genesis 1912 {
genesis 1913 char buffer[64];
genesis 1914 time_t now;
genesis 1915 time(&now);
genesis 1916 struct tm* now_gmt = gmtime(&now);
genesis 1917 string locale(setlocale(LC_TIME, NULL));
genesis 1918 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
genesis 1919 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
genesis 1920 setlocale(LC_TIME, locale.c_str());
genesis 1921 return string(buffer);
genesis 1922 }
genesis 1923
genesis 1924 static string HTTPReply(int nStatus, const string& strMsg)
genesis 1925 {
genesis 1926 if (nStatus == 401)
genesis 1927 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
genesis 1928 "Date: %s\r\n"
genesis 1929 "Server: bitcoin-json-rpc/%s\r\n"
genesis 1930 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
genesis 1931 "Content-Type: text/html\r\n"
genesis 1932 "Content-Length: 296\r\n"
genesis 1933 "\r\n"
genesis 1934 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
genesis 1935 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
genesis 1936 "<HTML>\r\n"
genesis 1937 "<HEAD>\r\n"
genesis 1938 "<TITLE>Error</TITLE>\r\n"
genesis 1939 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
genesis 1940 "</HEAD>\r\n"
genesis 1941 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
genesis 1942 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
genesis 1943 const char *cStatus;
genesis 1944 if (nStatus == 200) cStatus = "OK";
genesis 1945 else if (nStatus == 400) cStatus = "Bad Request";
genesis 1946 else if (nStatus == 403) cStatus = "Forbidden";
genesis 1947 else if (nStatus == 404) cStatus = "Not Found";
genesis 1948 else if (nStatus == 500) cStatus = "Internal Server Error";
genesis 1949 else cStatus = "";
genesis 1950 return strprintf(
genesis 1951 "HTTP/1.1 %d %s\r\n"
genesis 1952 "Date: %s\r\n"
genesis 1953 "Connection: close\r\n"
genesis 1954 "Content-Length: %d\r\n"
genesis 1955 "Content-Type: application/json\r\n"
genesis 1956 "Server: bitcoin-json-rpc/%s\r\n"
genesis 1957 "\r\n"
genesis 1958 "%s",
genesis 1959 nStatus,
genesis 1960 cStatus,
genesis 1961 rfc1123Time().c_str(),
genesis 1962 strMsg.size(),
genesis 1963 FormatFullVersion().c_str(),
genesis 1964 strMsg.c_str());
genesis 1965 }
genesis 1966
genesis 1967 int ReadHTTPStatus(std::basic_istream<char>& stream)
genesis 1968 {
genesis 1969 string str;
genesis 1970 getline(stream, str);
genesis 1971 vector<string> vWords;
genesis 1972 boost::split(vWords, str, boost::is_any_of(" "));
genesis 1973 if (vWords.size() < 2)
genesis 1974 return 500;
genesis 1975 return atoi(vWords[1].c_str());
genesis 1976 }
genesis 1977
genesis 1978 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
genesis 1979 {
genesis 1980 int nLen = 0;
genesis 1981 loop
genesis 1982 {
genesis 1983 string str;
genesis 1984 std::getline(stream, str);
genesis 1985 if (str.empty() || str == "\r")
genesis 1986 break;
genesis 1987 string::size_type nColon = str.find(":");
genesis 1988 if (nColon != string::npos)
genesis 1989 {
genesis 1990 string strHeader = str.substr(0, nColon);
genesis 1991 boost::trim(strHeader);
genesis 1992 boost::to_lower(strHeader);
genesis 1993 string strValue = str.substr(nColon+1);
genesis 1994 boost::trim(strValue);
genesis 1995 mapHeadersRet[strHeader] = strValue;
genesis 1996 if (strHeader == "content-length")
genesis 1997 nLen = atoi(strValue.c_str());
genesis 1998 }
genesis 1999 }
genesis 2000 return nLen;
genesis 2001 }
genesis 2002
genesis 2003 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
genesis 2004 {
genesis 2005 mapHeadersRet.clear();
genesis 2006 strMessageRet = "";
genesis 2007
genesis 2008 // Read status
genesis 2009 int nStatus = ReadHTTPStatus(stream);
genesis 2010
genesis 2011 // Read header
genesis 2012 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
genesis 2013 if (nLen < 0 || nLen > MAX_SIZE)
genesis 2014 return 500;
genesis 2015
genesis 2016 // Read message
genesis 2017 if (nLen > 0)
genesis 2018 {
genesis 2019 vector<char> vch(nLen);
genesis 2020 stream.read(&vch[0], nLen);
genesis 2021 strMessageRet = string(vch.begin(), vch.end());
genesis 2022 }
genesis 2023
genesis 2024 return nStatus;
genesis 2025 }
genesis 2026
genesis 2027 bool HTTPAuthorized(map<string, string>& mapHeaders)
genesis 2028 {
genesis 2029 string strAuth = mapHeaders["authorization"];
genesis 2030 if (strAuth.substr(0,6) != "Basic ")
genesis 2031 return false;
genesis 2032 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
genesis 2033 string strUserPass = DecodeBase64(strUserPass64);
genesis 2034 return strUserPass == strRPCUserColonPass;
genesis 2035 }
genesis 2036
genesis 2037 //
genesis 2038 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
genesis 2039 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
genesis 2040 // unspecified (HTTP errors and contents of 'error').
genesis 2041 //
genesis 2042 // 1.0 spec: http://json-rpc.org/wiki/specification
genesis 2043 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
genesis 2044 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
genesis 2045 //
genesis 2046
genesis 2047 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
genesis 2048 {
genesis 2049 Object request;
genesis 2050 request.push_back(Pair("method", strMethod));
genesis 2051 request.push_back(Pair("params", params));
genesis 2052 request.push_back(Pair("id", id));
genesis 2053 return write_string(Value(request), false) + "\n";
genesis 2054 }
genesis 2055
genesis 2056 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
genesis 2057 {
genesis 2058 Object reply;
genesis 2059 if (error.type() != null_type)
genesis 2060 reply.push_back(Pair("result", Value::null));
genesis 2061 else
genesis 2062 reply.push_back(Pair("result", result));
genesis 2063 reply.push_back(Pair("error", error));
genesis 2064 reply.push_back(Pair("id", id));
genesis 2065 return write_string(Value(reply), false) + "\n";
genesis 2066 }
genesis 2067
genesis 2068 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
genesis 2069 {
genesis 2070 // Send error reply from json-rpc error object
genesis 2071 int nStatus = 500;
genesis 2072 int code = find_value(objError, "code").get_int();
genesis 2073 if (code == -32600) nStatus = 400;
genesis 2074 else if (code == -32601) nStatus = 404;
genesis 2075 string strReply = JSONRPCReply(Value::null, objError, id);
genesis 2076 stream << HTTPReply(nStatus, strReply) << std::flush;
genesis 2077 }
genesis 2078
genesis 2079 bool ClientAllowed(const string& strAddress)
genesis 2080 {
genesis 2081 if (strAddress == asio::ip::address_v4::loopback().to_string())
genesis 2082 return true;
genesis 2083 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
genesis 2084 BOOST_FOREACH(string strAllow, vAllow)
genesis 2085 if (WildcardMatch(strAddress, strAllow))
genesis 2086 return true;
genesis 2087 return false;
genesis 2088 }
genesis 2089
genesis 2090 #ifdef USE_SSL
genesis 2091 //
genesis 2092 // IOStream device that speaks SSL but can also speak non-SSL
genesis 2093 //
genesis 2094 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
genesis 2095 public:
genesis 2096 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
genesis 2097 {
genesis 2098 fUseSSL = fUseSSLIn;
genesis 2099 fNeedHandshake = fUseSSLIn;
genesis 2100 }
genesis 2101
genesis 2102 void handshake(ssl::stream_base::handshake_type role)
genesis 2103 {
genesis 2104 if (!fNeedHandshake) return;
genesis 2105 fNeedHandshake = false;
genesis 2106 stream.handshake(role);
genesis 2107 }
genesis 2108 std::streamsize read(char* s, std::streamsize n)
genesis 2109 {
genesis 2110 handshake(ssl::stream_base::server); // HTTPS servers read first
genesis 2111 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
genesis 2112 return stream.next_layer().read_some(asio::buffer(s, n));
genesis 2113 }
genesis 2114 std::streamsize write(const char* s, std::streamsize n)
genesis 2115 {
genesis 2116 handshake(ssl::stream_base::client); // HTTPS clients write first
genesis 2117 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
genesis 2118 return asio::write(stream.next_layer(), asio::buffer(s, n));
genesis 2119 }
genesis 2120 bool connect(const std::string& server, const std::string& port)
genesis 2121 {
genesis 2122 ip::tcp::resolver resolver(stream.get_io_service());
genesis 2123 ip::tcp::resolver::query query(server.c_str(), port.c_str());
genesis 2124 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
genesis 2125 ip::tcp::resolver::iterator end;
genesis 2126 boost::system::error_code error = asio::error::host_not_found;
genesis 2127 while (error && endpoint_iterator != end)
genesis 2128 {
genesis 2129 stream.lowest_layer().close();
genesis 2130 stream.lowest_layer().connect(*endpoint_iterator++, error);
genesis 2131 }
genesis 2132 if (error)
genesis 2133 return false;
genesis 2134 return true;
genesis 2135 }
genesis 2136
genesis 2137 private:
genesis 2138 bool fNeedHandshake;
genesis 2139 bool fUseSSL;
genesis 2140 SSLStream& stream;
genesis 2141 };
genesis 2142 #endif
genesis 2143
genesis 2144 void ThreadRPCServer(void* parg)
genesis 2145 {
genesis 2146 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
genesis 2147 try
genesis 2148 {
genesis 2149 vnThreadsRunning[4]++;
genesis 2150 ThreadRPCServer2(parg);
genesis 2151 vnThreadsRunning[4]--;
genesis 2152 }
genesis 2153 catch (std::exception& e) {
genesis 2154 vnThreadsRunning[4]--;
genesis 2155 PrintException(&e, "ThreadRPCServer()");
genesis 2156 } catch (...) {
genesis 2157 vnThreadsRunning[4]--;
genesis 2158 PrintException(NULL, "ThreadRPCServer()");
genesis 2159 }
genesis 2160 printf("ThreadRPCServer exiting\n");
genesis 2161 }
genesis 2162
genesis 2163 void ThreadRPCServer2(void* parg)
genesis 2164 {
genesis 2165 printf("ThreadRPCServer started\n");
genesis 2166
genesis 2167 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
genesis 2168 if (strRPCUserColonPass == ":")
genesis 2169 {
genesis 2170 unsigned char rand_pwd[32];
genesis 2171 RAND_bytes(rand_pwd, 32);
genesis 2172 string strWhatAmI = "To use bitcoind";
genesis 2173 if (mapArgs.count("-server"))
genesis 2174 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
genesis 2175 else if (mapArgs.count("-daemon"))
genesis 2176 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
genesis 2177 PrintConsole(
genesis 2178 _("Error: %s, you must set a rpcpassword in the configuration file:\n %s\n"
genesis 2179 "It is recommended you use the following random password:\n"
genesis 2180 "rpcuser=bitcoinrpc\n"
genesis 2181 "rpcpassword=%s\n"
genesis 2182 "(you do not need to remember this password)\n"
genesis 2183 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
genesis 2184 strWhatAmI.c_str(),
genesis 2185 GetConfigFile().c_str(),
genesis 2186 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str());
genesis 2187 #ifndef QT_GUI
genesis 2188 CreateThread(Shutdown, NULL);
genesis 2189 #endif
genesis 2190 return;
genesis 2191 }
genesis 2192
genesis 2193 bool fUseSSL = GetBoolArg("-rpcssl");
genesis 2194 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
genesis 2195
genesis 2196 asio::io_service io_service;
genesis 2197 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
genesis 2198 ip::tcp::acceptor acceptor(io_service, endpoint);
genesis 2199
genesis 2200 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
genesis 2201
genesis 2202 #ifdef USE_SSL
genesis 2203 ssl::context context(io_service, ssl::context::sslv23);
genesis 2204 if (fUseSSL)
genesis 2205 {
genesis 2206 context.set_options(ssl::context::no_sslv2);
genesis 2207 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
genesis 2208 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
genesis 2209 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
genesis 2210 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
genesis 2211 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
genesis 2212 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
genesis 2213 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
genesis 2214 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
genesis 2215
genesis 2216 string ciphers = GetArg("-rpcsslciphers",
genesis 2217 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
genesis 2218 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
genesis 2219 }
genesis 2220 #else
genesis 2221 if (fUseSSL)
genesis 2222 throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
genesis 2223 #endif
genesis 2224
genesis 2225 loop
genesis 2226 {
genesis 2227 // Accept connection
genesis 2228 #ifdef USE_SSL
genesis 2229 SSLStream sslStream(io_service, context);
genesis 2230 SSLIOStreamDevice d(sslStream, fUseSSL);
genesis 2231 iostreams::stream<SSLIOStreamDevice> stream(d);
genesis 2232 #else
genesis 2233 ip::tcp::iostream stream;
genesis 2234 #endif
genesis 2235
genesis 2236 ip::tcp::endpoint peer;
genesis 2237 vnThreadsRunning[4]--;
genesis 2238 #ifdef USE_SSL
genesis 2239 acceptor.accept(sslStream.lowest_layer(), peer);
genesis 2240 #else
genesis 2241 acceptor.accept(*stream.rdbuf(), peer);
genesis 2242 #endif
genesis 2243 vnThreadsRunning[4]++;
genesis 2244 if (fShutdown)
genesis 2245 return;
genesis 2246
genesis 2247 // Restrict callers by IP
genesis 2248 if (!ClientAllowed(peer.address().to_string()))
genesis 2249 {
genesis 2250 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
genesis 2251 if (!fUseSSL)
genesis 2252 stream << HTTPReply(403, "") << std::flush;
genesis 2253 continue;
genesis 2254 }
genesis 2255
genesis 2256 map<string, string> mapHeaders;
genesis 2257 string strRequest;
genesis 2258
genesis 2259 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
genesis 2260 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
genesis 2261 { // Timed out:
genesis 2262 acceptor.cancel();
genesis 2263 printf("ThreadRPCServer ReadHTTP timeout\n");
genesis 2264 continue;
genesis 2265 }
genesis 2266
genesis 2267 // Check authorization
genesis 2268 if (mapHeaders.count("authorization") == 0)
genesis 2269 {
genesis 2270 stream << HTTPReply(401, "") << std::flush;
genesis 2271 continue;
genesis 2272 }
genesis 2273 if (!HTTPAuthorized(mapHeaders))
genesis 2274 {
genesis 2275 printf("ThreadRPCServer incorrect password attempt from %s\n",peer.address().to_string().c_str());
genesis 2276 /* Deter brute-forcing short passwords.
genesis 2277 If this results in a DOS the user really
genesis 2278 shouldn't have their RPC port exposed.*/
genesis 2279 if (mapArgs["-rpcpassword"].size() < 20)
genesis 2280 Sleep(250);
genesis 2281
genesis 2282 stream << HTTPReply(401, "") << std::flush;
genesis 2283 continue;
genesis 2284 }
genesis 2285
genesis 2286 Value id = Value::null;
genesis 2287 try
genesis 2288 {
genesis 2289 // Parse request
genesis 2290 Value valRequest;
genesis 2291 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
genesis 2292 throw JSONRPCError(-32700, "Parse error");
genesis 2293 const Object& request = valRequest.get_obj();
genesis 2294
genesis 2295 // Parse id now so errors from here on will have the id
genesis 2296 id = find_value(request, "id");
genesis 2297
genesis 2298 // Parse method
genesis 2299 Value valMethod = find_value(request, "method");
genesis 2300 if (valMethod.type() == null_type)
genesis 2301 throw JSONRPCError(-32600, "Missing method");
genesis 2302 if (valMethod.type() != str_type)
genesis 2303 throw JSONRPCError(-32600, "Method must be a string");
genesis 2304 string strMethod = valMethod.get_str();
genesis 2305 if (strMethod != "getwork" && strMethod != "getmemorypool")
genesis 2306 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
genesis 2307
genesis 2308 // Parse params
genesis 2309 Value valParams = find_value(request, "params");
genesis 2310 Array params;
genesis 2311 if (valParams.type() == array_type)
genesis 2312 params = valParams.get_array();
genesis 2313 else if (valParams.type() == null_type)
genesis 2314 params = Array();
genesis 2315 else
genesis 2316 throw JSONRPCError(-32600, "Params must be an array");
genesis 2317
genesis 2318 // Find method
genesis 2319 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
genesis 2320 if (mi == mapCallTable.end())
genesis 2321 throw JSONRPCError(-32601, "Method not found");
genesis 2322
genesis 2323 // Observe safe mode
genesis 2324 string strWarning = GetWarnings("rpc");
genesis 2325 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
genesis 2326 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
genesis 2327
genesis 2328 try
genesis 2329 {
genesis 2330 // Execute
genesis 2331 Value result;
genesis 2332 CRITICAL_BLOCK(cs_main)
genesis 2333 CRITICAL_BLOCK(pwalletMain->cs_wallet)
genesis 2334 result = (*(*mi).second)(params, false);
genesis 2335
genesis 2336 // Send reply
genesis 2337 string strReply = JSONRPCReply(result, Value::null, id);
genesis 2338 stream << HTTPReply(200, strReply) << std::flush;
genesis 2339 }
genesis 2340 catch (std::exception& e)
genesis 2341 {
genesis 2342 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
genesis 2343 }
genesis 2344 }
genesis 2345 catch (Object& objError)
genesis 2346 {
genesis 2347 ErrorReply(stream, objError, id);
genesis 2348 }
genesis 2349 catch (std::exception& e)
genesis 2350 {
genesis 2351 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
genesis 2352 }
genesis 2353 }
genesis 2354 }
genesis 2355
genesis 2356
genesis 2357
genesis 2358
genesis 2359 Object CallRPC(const string& strMethod, const Array& params)
genesis 2360 {
genesis 2361 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
genesis 2362 throw runtime_error(strprintf(
genesis 2363 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
genesis 2364 "If the file does not exist, create it with owner-readable-only file permissions."),
genesis 2365 GetConfigFile().c_str()));
genesis 2366
genesis 2367 // Connect to localhost
genesis 2368 bool fUseSSL = GetBoolArg("-rpcssl");
genesis 2369 #ifdef USE_SSL
genesis 2370 asio::io_service io_service;
genesis 2371 ssl::context context(io_service, ssl::context::sslv23);
genesis 2372 context.set_options(ssl::context::no_sslv2);
genesis 2373 SSLStream sslStream(io_service, context);
genesis 2374 SSLIOStreamDevice d(sslStream, fUseSSL);
genesis 2375 iostreams::stream<SSLIOStreamDevice> stream(d);
genesis 2376 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
genesis 2377 throw runtime_error("couldn't connect to server");
genesis 2378 #else
genesis 2379 if (fUseSSL)
genesis 2380 throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
genesis 2381
genesis 2382 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
genesis 2383 if (stream.fail())
genesis 2384 throw runtime_error("couldn't connect to server");
genesis 2385 #endif
genesis 2386
genesis 2387
genesis 2388 // HTTP basic authentication
genesis 2389 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
genesis 2390 map<string, string> mapRequestHeaders;
genesis 2391 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
genesis 2392
genesis 2393 // Send request
genesis 2394 string strRequest = JSONRPCRequest(strMethod, params, 1);
genesis 2395 string strPost = HTTPPost(strRequest, mapRequestHeaders);
genesis 2396 stream << strPost << std::flush;
genesis 2397
genesis 2398 // Receive reply
genesis 2399 map<string, string> mapHeaders;
genesis 2400 string strReply;
genesis 2401 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
genesis 2402 if (nStatus == 401)
genesis 2403 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
genesis 2404 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
genesis 2405 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
genesis 2406 else if (strReply.empty())
genesis 2407 throw runtime_error("no response from server");
genesis 2408
genesis 2409 // Parse reply
genesis 2410 Value valReply;
genesis 2411 if (!read_string(strReply, valReply))
genesis 2412 throw runtime_error("couldn't parse reply from server");
genesis 2413 const Object& reply = valReply.get_obj();
genesis 2414 if (reply.empty())
genesis 2415 throw runtime_error("expected reply to have result, error and id properties");
genesis 2416
genesis 2417 return reply;
genesis 2418 }
genesis 2419
genesis 2420
genesis 2421
genesis 2422
genesis 2423 template<typename T>
genesis 2424 void ConvertTo(Value& value)
genesis 2425 {
genesis 2426 if (value.type() == str_type)
genesis 2427 {
genesis 2428 // reinterpret string as unquoted json value
genesis 2429 Value value2;
genesis 2430 if (!read_string(value.get_str(), value2))
genesis 2431 throw runtime_error("type mismatch");
genesis 2432 value = value2.get_value<T>();
genesis 2433 }
genesis 2434 else
genesis 2435 {
genesis 2436 value = value.get_value<T>();
genesis 2437 }
genesis 2438 }
genesis 2439
genesis 2440 int CommandLineRPC(int argc, char *argv[])
genesis 2441 {
genesis 2442 string strPrint;
genesis 2443 int nRet = 0;
genesis 2444 try
genesis 2445 {
genesis 2446 // Skip switches
genesis 2447 while (argc > 1 && IsSwitchChar(argv[1][0]))
genesis 2448 {
genesis 2449 argc--;
genesis 2450 argv++;
genesis 2451 }
genesis 2452
genesis 2453 // Method
genesis 2454 if (argc < 2)
genesis 2455 throw runtime_error("too few parameters");
genesis 2456 string strMethod = argv[1];
genesis 2457
genesis 2458 // Parameters default to strings
genesis 2459 Array params;
genesis 2460 for (int i = 2; i < argc; i++)
genesis 2461 params.push_back(argv[i]);
genesis 2462 int n = params.size();
genesis 2463
genesis 2464 //
genesis 2465 // Special case non-string parameter types
genesis 2466 //
genesis 2467 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
genesis 2468 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2469 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
genesis 2470 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
genesis 2471 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2472 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2473 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
genesis 2474 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
genesis 2475 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
genesis 2476 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
genesis 2477 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2478 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
genesis 2479 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
genesis 2480 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
genesis 2481 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
genesis 2482 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2483 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
genesis 2484 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
genesis 2485 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2486 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
genesis 2487 if (strMethod == "sendmany" && n > 1)
genesis 2488 {
genesis 2489 string s = params[1].get_str();
genesis 2490 Value v;
genesis 2491 if (!read_string(s, v) || v.type() != obj_type)
genesis 2492 throw runtime_error("type mismatch");
genesis 2493 params[1] = v.get_obj();
genesis 2494 }
genesis 2495 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
genesis 2496
genesis 2497 // Execute
genesis 2498 Object reply = CallRPC(strMethod, params);
genesis 2499
genesis 2500 // Parse reply
genesis 2501 const Value& result = find_value(reply, "result");
genesis 2502 const Value& error = find_value(reply, "error");
genesis 2503
genesis 2504 if (error.type() != null_type)
genesis 2505 {
genesis 2506 // Error
genesis 2507 strPrint = "error: " + write_string(error, false);
genesis 2508 int code = find_value(error.get_obj(), "code").get_int();
genesis 2509 nRet = abs(code);
genesis 2510 }
genesis 2511 else
genesis 2512 {
genesis 2513 // Result
genesis 2514 if (result.type() == null_type)
genesis 2515 strPrint = "";
genesis 2516 else if (result.type() == str_type)
genesis 2517 strPrint = result.get_str();
genesis 2518 else
genesis 2519 strPrint = write_string(result, true);
genesis 2520 }
genesis 2521 }
genesis 2522 catch (std::exception& e)
genesis 2523 {
genesis 2524 strPrint = string("error: ") + e.what();
genesis 2525 nRet = 87;
genesis 2526 }
genesis 2527 catch (...)
genesis 2528 {
genesis 2529 PrintException(NULL, "CommandLineRPC()");
genesis 2530 }
genesis 2531
genesis 2532 if (strPrint != "")
genesis 2533 {
genesis 2534 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
genesis 2535 }
genesis 2536 return nRet;
genesis 2537 }
genesis 2538
genesis 2539
genesis 2540
genesis 2541
genesis 2542 #ifdef TEST
genesis 2543 int main(int argc, char *argv[])
genesis 2544 {
genesis 2545 #ifdef _MSC_VER
genesis 2546 // Turn off microsoft heap dump noise
genesis 2547 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
genesis 2548 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
genesis 2549 #endif
genesis 2550 setbuf(stdin, NULL);
genesis 2551 setbuf(stdout, NULL);
genesis 2552 setbuf(stderr, NULL);
genesis 2553
genesis 2554 try
genesis 2555 {
genesis 2556 if (argc >= 2 && string(argv[1]) == "-server")
genesis 2557 {
genesis 2558 printf("server ready\n");
genesis 2559 ThreadRPCServer(NULL);
genesis 2560 }
genesis 2561 else
genesis 2562 {
genesis 2563 return CommandLineRPC(argc, argv);
genesis 2564 }
genesis 2565 }
genesis 2566 catch (std::exception& e) {
genesis 2567 PrintException(&e, "main()");
genesis 2568 } catch (...) {
genesis 2569 PrintException(NULL, "main()");
genesis 2570 }
genesis 2571 return 0;
genesis 2572 }
genesis 2573 #endif