-
+ FB6F99C010956F5A04DB684A1C15D83A891147213363873012A9460B388C929F6BCF83F09DD246482662261A91FEE19376F484B2C9A39A072D3C5213DCAF29A2
bitcoin/src/bitcoinrpc.cpp
(0 . 0)(1 . 2604)
1046 // /****************************\
1047 // * EXPERIMENTAL BRANCH. *
1048 // * FOR LABORATORY USE ONLY. *
1049 // ********************************
1050 // ************
1051 // **************
1052 // ****************
1053 // **** **** ****
1054 // *** *** ***
1055 // *** *** ***
1056 // *** * * **
1057 // ******** ********
1058 // ******* ******
1059 // *** **
1060 // * ******* **
1061 // ** * * * * *
1062 // ** * * ***
1063 // **** * * * * ****
1064 // **** *** * * ** ***
1065 // **** ********* ******
1066 // ******* ***** *******
1067 // ********* ****** **
1068 // ** ****** ******
1069 // ** ******* **
1070 // ** ******* ***
1071 // **** ******** ************
1072 // ************ ************
1073 // ******** *******
1074 // ****** ****
1075 // *** ***
1076 // ********************************
1077 // Copyright (c) 2010 Satoshi Nakamoto
1078 // Copyright (c) 2009-2012 The Bitcoin developers
1079 // Distributed under the MIT/X11 software license, see the accompanying
1080 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
1081
1082 #include "headers.h"
1083 #include "db.h"
1084 #include "net.h"
1085 #include "init.h"
1086 #undef printf
1087 #include <boost/asio.hpp>
1088 #include <boost/iostreams/concepts.hpp>
1089 #include <boost/iostreams/stream.hpp>
1090 #include <boost/algorithm/string.hpp>
1091 #ifdef USE_SSL
1092 #include <boost/asio/ssl.hpp>
1093 #include <boost/filesystem.hpp>
1094 #include <boost/filesystem/fstream.hpp>
1095 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
1096 #endif
1097 #include "json/json_spirit_reader_template.h"
1098 #include "json/json_spirit_writer_template.h"
1099 #include "json/json_spirit_utils.h"
1100 #define printf OutputDebugStringF
1101 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
1102 // precompiled in headers.h. The problem might be when the pch file goes over
1103 // a certain size around 145MB. If we need access to json_spirit outside this
1104 // file, we could use the compiled json_spirit option.
1105
1106 using namespace std;
1107 using namespace boost;
1108 using namespace boost::asio;
1109 using namespace json_spirit;
1110
1111 void ThreadRPCServer2(void* parg);
1112 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
1113 extern map<string, rpcfn_type> mapCallTable;
1114
1115 static std::string strRPCUserColonPass;
1116
1117 static int64 nWalletUnlockTime;
1118 static CCriticalSection cs_nWalletUnlockTime;
1119
1120
1121 Object JSONRPCError(int code, const string& message)
1122 {
1123 Object error;
1124 error.push_back(Pair("code", code));
1125 error.push_back(Pair("message", message));
1126 return error;
1127 }
1128
1129
1130 void PrintConsole(const std::string &format, ...)
1131 {
1132 char buffer[50000];
1133 int limit = sizeof(buffer);
1134 va_list arg_ptr;
1135 va_start(arg_ptr, format);
1136 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
1137 va_end(arg_ptr);
1138 if (ret < 0 || ret >= limit)
1139 {
1140 ret = limit - 1;
1141 buffer[limit-1] = 0;
1142 }
1143 printf("%s", buffer);
1144 fprintf(stdout, "%s", buffer);
1145 }
1146
1147
1148 int64 AmountFromValue(const Value& value)
1149 {
1150 double dAmount = value.get_real();
1151 if (dAmount <= 0.0 || dAmount > 21000000.0)
1152 throw JSONRPCError(-3, "Invalid amount");
1153 int64 nAmount = roundint64(dAmount * COIN);
1154 if (!MoneyRange(nAmount))
1155 throw JSONRPCError(-3, "Invalid amount");
1156 return nAmount;
1157 }
1158
1159 Value ValueFromAmount(int64 amount)
1160 {
1161 return (double)amount / (double)COIN;
1162 }
1163
1164 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
1165 {
1166 entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
1167 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
1168 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
1169 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
1170 entry.push_back(Pair(item.first, item.second));
1171 }
1172
1173 string AccountFromValue(const Value& value)
1174 {
1175 string strAccount = value.get_str();
1176 if (strAccount == "*")
1177 throw JSONRPCError(-11, "Invalid account name");
1178 return strAccount;
1179 }
1180
1181
1182
1183 ///
1184 /// Note: This interface may still be subject to change.
1185 ///
1186
1187
1188 Value help(const Array& params, bool fHelp)
1189 {
1190 if (fHelp || params.size() > 1)
1191 throw runtime_error(
1192 "help [command]\n"
1193 "List commands, or get help for a command.");
1194
1195 string strCommand;
1196 if (params.size() > 0)
1197 strCommand = params[0].get_str();
1198
1199 string strRet;
1200 set<rpcfn_type> setDone;
1201 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
1202 {
1203 string strMethod = (*mi).first;
1204 // We already filter duplicates, but these deprecated screw up the sort order
1205 if (strMethod == "getamountreceived" ||
1206 strMethod == "getallreceived" ||
1207 strMethod == "getblocknumber" || // deprecated
1208 (strMethod.find("label") != string::npos))
1209 continue;
1210 if (strCommand != "" && strMethod != strCommand)
1211 continue;
1212 try
1213 {
1214 Array params;
1215 rpcfn_type pfn = (*mi).second;
1216 if (setDone.insert(pfn).second)
1217 (*pfn)(params, true);
1218 }
1219 catch (std::exception& e)
1220 {
1221 // Help text is returned in an exception
1222 string strHelp = string(e.what());
1223 if (strCommand == "")
1224 if (strHelp.find('\n') != -1)
1225 strHelp = strHelp.substr(0, strHelp.find('\n'));
1226 strRet += strHelp + "\n";
1227 }
1228 }
1229 if (strRet == "")
1230 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
1231 strRet = strRet.substr(0,strRet.size()-1);
1232 return strRet;
1233 }
1234
1235
1236 Value stop(const Array& params, bool fHelp)
1237 {
1238 if (fHelp || params.size() != 0)
1239 throw runtime_error(
1240 "stop\n"
1241 "Stop bitcoin server.");
1242 #ifndef QT_GUI
1243 // Shutdown will take long enough that the response should get back
1244 CreateThread(Shutdown, NULL);
1245 return "bitcoin server stopping";
1246 #else
1247 throw runtime_error("NYI: cannot shut down GUI with RPC command");
1248 #endif
1249 }
1250
1251
1252 Value getblockcount(const Array& params, bool fHelp)
1253 {
1254 if (fHelp || params.size() != 0)
1255 throw runtime_error(
1256 "getblockcount\n"
1257 "Returns the number of blocks in the longest block chain.");
1258
1259 return nBestHeight;
1260 }
1261
1262
1263 // deprecated
1264 Value getblocknumber(const Array& params, bool fHelp)
1265 {
1266 if (fHelp || params.size() != 0)
1267 throw runtime_error(
1268 "getblocknumber\n"
1269 "Deprecated. Use getblockcount.");
1270
1271 return nBestHeight;
1272 }
1273
1274
1275 Value getconnectioncount(const Array& params, bool fHelp)
1276 {
1277 if (fHelp || params.size() != 0)
1278 throw runtime_error(
1279 "getconnectioncount\n"
1280 "Returns the number of connections to other nodes.");
1281
1282 return (int)vNodes.size();
1283 }
1284
1285
1286 double GetDifficulty()
1287 {
1288 // Floating point number that is a multiple of the minimum difficulty,
1289 // minimum difficulty = 1.0.
1290
1291 if (pindexBest == NULL)
1292 return 1.0;
1293 int nShift = (pindexBest->nBits >> 24) & 0xff;
1294
1295 double dDiff =
1296 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
1297
1298 while (nShift < 29)
1299 {
1300 dDiff *= 256.0;
1301 nShift++;
1302 }
1303 while (nShift > 29)
1304 {
1305 dDiff /= 256.0;
1306 nShift--;
1307 }
1308
1309 return dDiff;
1310 }
1311
1312 Value getdifficulty(const Array& params, bool fHelp)
1313 {
1314 if (fHelp || params.size() != 0)
1315 throw runtime_error(
1316 "getdifficulty\n"
1317 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
1318
1319 return GetDifficulty();
1320 }
1321
1322
1323 Value getgenerate(const Array& params, bool fHelp)
1324 {
1325 if (fHelp || params.size() != 0)
1326 throw runtime_error(
1327 "getgenerate\n"
1328 "Returns true or false.");
1329
1330 return (bool)fGenerateBitcoins;
1331 }
1332
1333
1334 Value setgenerate(const Array& params, bool fHelp)
1335 {
1336 if (fHelp || params.size() < 1 || params.size() > 2)
1337 throw runtime_error(
1338 "setgenerate <generate> [genproclimit]\n"
1339 "<generate> is true or false to turn generation on or off.\n"
1340 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
1341
1342 bool fGenerate = true;
1343 if (params.size() > 0)
1344 fGenerate = params[0].get_bool();
1345
1346 if (params.size() > 1)
1347 {
1348 int nGenProcLimit = params[1].get_int();
1349 fLimitProcessors = (nGenProcLimit != -1);
1350 WriteSetting("fLimitProcessors", fLimitProcessors);
1351 if (nGenProcLimit != -1)
1352 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
1353 if (nGenProcLimit == 0)
1354 fGenerate = false;
1355 }
1356
1357 GenerateBitcoins(fGenerate, pwalletMain);
1358 return Value::null;
1359 }
1360
1361
1362 Value gethashespersec(const Array& params, bool fHelp)
1363 {
1364 if (fHelp || params.size() != 0)
1365 throw runtime_error(
1366 "gethashespersec\n"
1367 "Returns a recent hashes per second performance measurement while generating.");
1368
1369 if (GetTimeMillis() - nHPSTimerStart > 8000)
1370 return (boost::int64_t)0;
1371 return (boost::int64_t)dHashesPerSec;
1372 }
1373
1374
1375 Value getinfo(const Array& params, bool fHelp)
1376 {
1377 if (fHelp || params.size() != 0)
1378 throw runtime_error(
1379 "getinfo\n"
1380 "Returns an object containing various state info.");
1381
1382 Object obj;
1383 obj.push_back(Pair("version", (int)VERSION));
1384 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
1385 obj.push_back(Pair("blocks", (int)nBestHeight));
1386 obj.push_back(Pair("connections", (int)vNodes.size()));
1387 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
1388 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
1389 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
1390 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
1391 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
1392 obj.push_back(Pair("testnet", fTestNet));
1393 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
1394 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
1395 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
1396 if (pwalletMain->IsCrypted())
1397 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
1398 obj.push_back(Pair("errors", GetWarnings("statusbar")));
1399 return obj;
1400 }
1401
1402
1403 Value getnewaddress(const Array& params, bool fHelp)
1404 {
1405 if (fHelp || params.size() > 1)
1406 throw runtime_error(
1407 "getnewaddress [account]\n"
1408 "Returns a new bitcoin address for receiving payments. "
1409 "If [account] is specified (recommended), it is added to the address book "
1410 "so payments received with the address will be credited to [account].");
1411
1412 // Parse the account first so we don't generate a key if there's an error
1413 string strAccount;
1414 if (params.size() > 0)
1415 strAccount = AccountFromValue(params[0]);
1416
1417 if (!pwalletMain->IsLocked())
1418 pwalletMain->TopUpKeyPool();
1419
1420 // Generate a new key that is added to wallet
1421 std::vector<unsigned char> newKey;
1422 if (!pwalletMain->GetKeyFromPool(newKey, false))
1423 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
1424 CBitcoinAddress address(newKey);
1425
1426 pwalletMain->SetAddressBookName(address, strAccount);
1427
1428 return address.ToString();
1429 }
1430
1431
1432 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
1433 {
1434 CWalletDB walletdb(pwalletMain->strWalletFile);
1435
1436 CAccount account;
1437 walletdb.ReadAccount(strAccount, account);
1438
1439 bool bKeyUsed = false;
1440
1441 // Check if the current key has been used
1442 if (!account.vchPubKey.empty())
1443 {
1444 CScript scriptPubKey;
1445 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
1446 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
1447 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
1448 ++it)
1449 {
1450 const CWalletTx& wtx = (*it).second;
1451 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1452 if (txout.scriptPubKey == scriptPubKey)
1453 bKeyUsed = true;
1454 }
1455 }
1456
1457 // Generate a new key
1458 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
1459 {
1460 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
1461 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
1462
1463 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
1464 walletdb.WriteAccount(strAccount, account);
1465 }
1466
1467 return CBitcoinAddress(account.vchPubKey);
1468 }
1469
1470 Value getaccountaddress(const Array& params, bool fHelp)
1471 {
1472 if (fHelp || params.size() != 1)
1473 throw runtime_error(
1474 "getaccountaddress <account>\n"
1475 "Returns the current bitcoin address for receiving payments to this account.");
1476
1477 // Parse the account first so we don't generate a key if there's an error
1478 string strAccount = AccountFromValue(params[0]);
1479
1480 Value ret;
1481
1482 ret = GetAccountAddress(strAccount).ToString();
1483
1484 return ret;
1485 }
1486
1487
1488
1489 Value setaccount(const Array& params, bool fHelp)
1490 {
1491 if (fHelp || params.size() < 1 || params.size() > 2)
1492 throw runtime_error(
1493 "setaccount <bitcoinaddress> <account>\n"
1494 "Sets the account associated with the given address.");
1495
1496 CBitcoinAddress address(params[0].get_str());
1497 if (!address.IsValid())
1498 throw JSONRPCError(-5, "Invalid bitcoin address");
1499
1500
1501 string strAccount;
1502 if (params.size() > 1)
1503 strAccount = AccountFromValue(params[1]);
1504
1505 // Detect when changing the account of an address that is the 'unused current key' of another account:
1506 if (pwalletMain->mapAddressBook.count(address))
1507 {
1508 string strOldAccount = pwalletMain->mapAddressBook[address];
1509 if (address == GetAccountAddress(strOldAccount))
1510 GetAccountAddress(strOldAccount, true);
1511 }
1512
1513 pwalletMain->SetAddressBookName(address, strAccount);
1514
1515 return Value::null;
1516 }
1517
1518
1519 Value getaccount(const Array& params, bool fHelp)
1520 {
1521 if (fHelp || params.size() != 1)
1522 throw runtime_error(
1523 "getaccount <bitcoinaddress>\n"
1524 "Returns the account associated with the given address.");
1525
1526 CBitcoinAddress address(params[0].get_str());
1527 if (!address.IsValid())
1528 throw JSONRPCError(-5, "Invalid bitcoin address");
1529
1530 string strAccount;
1531 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
1532 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
1533 strAccount = (*mi).second;
1534 return strAccount;
1535 }
1536
1537
1538 Value getaddressesbyaccount(const Array& params, bool fHelp)
1539 {
1540 if (fHelp || params.size() != 1)
1541 throw runtime_error(
1542 "getaddressesbyaccount <account>\n"
1543 "Returns the list of addresses for the given account.");
1544
1545 string strAccount = AccountFromValue(params[0]);
1546
1547 // Find all addresses that have the given account
1548 Array ret;
1549 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
1550 {
1551 const CBitcoinAddress& address = item.first;
1552 const string& strName = item.second;
1553 if (strName == strAccount)
1554 ret.push_back(address.ToString());
1555 }
1556 return ret;
1557 }
1558
1559 Value settxfee(const Array& params, bool fHelp)
1560 {
1561 if (fHelp || params.size() < 1 || params.size() > 1)
1562 throw runtime_error(
1563 "settxfee <amount>\n"
1564 "<amount> is a real and is rounded to the nearest 0.00000001");
1565
1566 // Amount
1567 int64 nAmount = 0;
1568 if (params[0].get_real() != 0.0)
1569 nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
1570
1571 nTransactionFee = nAmount;
1572 return true;
1573 }
1574
1575 Value sendtoaddress(const Array& params, bool fHelp)
1576 {
1577 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
1578 throw runtime_error(
1579 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
1580 "<amount> is a real and is rounded to the nearest 0.00000001\n"
1581 "requires wallet passphrase to be set with walletpassphrase first");
1582 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
1583 throw runtime_error(
1584 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
1585 "<amount> is a real and is rounded to the nearest 0.00000001");
1586
1587 CBitcoinAddress address(params[0].get_str());
1588 if (!address.IsValid())
1589 throw JSONRPCError(-5, "Invalid bitcoin address");
1590
1591 // Amount
1592 int64 nAmount = AmountFromValue(params[1]);
1593
1594 // Wallet comments
1595 CWalletTx wtx;
1596 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
1597 wtx.mapValue["comment"] = params[2].get_str();
1598 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
1599 wtx.mapValue["to"] = params[3].get_str();
1600
1601 if (pwalletMain->IsLocked())
1602 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1603
1604 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
1605 if (strError != "")
1606 throw JSONRPCError(-4, strError);
1607
1608 return wtx.GetHash().GetHex();
1609 }
1610
1611 static const string strMessageMagic = "Bitcoin Signed Message:\n";
1612
1613 Value signmessage(const Array& params, bool fHelp)
1614 {
1615 if (fHelp || params.size() != 2)
1616 throw runtime_error(
1617 "signmessage <bitcoinaddress> <message>\n"
1618 "Sign a message with the private key of an address");
1619
1620 if (pwalletMain->IsLocked())
1621 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1622
1623 string strAddress = params[0].get_str();
1624 string strMessage = params[1].get_str();
1625
1626 CBitcoinAddress addr(strAddress);
1627 if (!addr.IsValid())
1628 throw JSONRPCError(-3, "Invalid address");
1629
1630 CKey key;
1631 if (!pwalletMain->GetKey(addr, key))
1632 throw JSONRPCError(-4, "Private key not available");
1633
1634 CDataStream ss(SER_GETHASH);
1635 ss << strMessageMagic;
1636 ss << strMessage;
1637
1638 vector<unsigned char> vchSig;
1639 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
1640 throw JSONRPCError(-5, "Sign failed");
1641
1642 return EncodeBase64(&vchSig[0], vchSig.size());
1643 }
1644
1645 Value verifymessage(const Array& params, bool fHelp)
1646 {
1647 if (fHelp || params.size() != 3)
1648 throw runtime_error(
1649 "verifymessage <bitcoinaddress> <signature> <message>\n"
1650 "Verify a signed message");
1651
1652 string strAddress = params[0].get_str();
1653 string strSign = params[1].get_str();
1654 string strMessage = params[2].get_str();
1655
1656 CBitcoinAddress addr(strAddress);
1657 if (!addr.IsValid())
1658 throw JSONRPCError(-3, "Invalid address");
1659
1660 bool fInvalid = false;
1661 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
1662
1663 if (fInvalid)
1664 throw JSONRPCError(-5, "Malformed base64 encoding");
1665
1666 CDataStream ss(SER_GETHASH);
1667 ss << strMessageMagic;
1668 ss << strMessage;
1669
1670 CKey key;
1671 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
1672 return false;
1673
1674 return (key.GetAddress() == addr);
1675 }
1676
1677
1678 Value getreceivedbyaddress(const Array& params, bool fHelp)
1679 {
1680 if (fHelp || params.size() < 1 || params.size() > 2)
1681 throw runtime_error(
1682 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
1683 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
1684
1685 // Bitcoin address
1686 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
1687 CScript scriptPubKey;
1688 if (!address.IsValid())
1689 throw JSONRPCError(-5, "Invalid bitcoin address");
1690 scriptPubKey.SetBitcoinAddress(address);
1691 if (!IsMine(*pwalletMain,scriptPubKey))
1692 return (double)0.0;
1693
1694 // Minimum confirmations
1695 int nMinDepth = 1;
1696 if (params.size() > 1)
1697 nMinDepth = params[1].get_int();
1698
1699 // Tally
1700 int64 nAmount = 0;
1701 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1702 {
1703 const CWalletTx& wtx = (*it).second;
1704 if (wtx.IsCoinBase() || !wtx.IsFinal())
1705 continue;
1706
1707 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1708 if (txout.scriptPubKey == scriptPubKey)
1709 if (wtx.GetDepthInMainChain() >= nMinDepth)
1710 nAmount += txout.nValue;
1711 }
1712
1713 return ValueFromAmount(nAmount);
1714 }
1715
1716
1717 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
1718 {
1719 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
1720 {
1721 const CBitcoinAddress& address = item.first;
1722 const string& strName = item.second;
1723 if (strName == strAccount)
1724 setAddress.insert(address);
1725 }
1726 }
1727
1728
1729 Value getreceivedbyaccount(const Array& params, bool fHelp)
1730 {
1731 if (fHelp || params.size() < 1 || params.size() > 2)
1732 throw runtime_error(
1733 "getreceivedbyaccount <account> [minconf=1]\n"
1734 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
1735
1736 // Minimum confirmations
1737 int nMinDepth = 1;
1738 if (params.size() > 1)
1739 nMinDepth = params[1].get_int();
1740
1741 // Get the set of pub keys that have the label
1742 string strAccount = AccountFromValue(params[0]);
1743 set<CBitcoinAddress> setAddress;
1744 GetAccountAddresses(strAccount, setAddress);
1745
1746 // Tally
1747 int64 nAmount = 0;
1748 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1749 {
1750 const CWalletTx& wtx = (*it).second;
1751 if (wtx.IsCoinBase() || !wtx.IsFinal())
1752 continue;
1753
1754 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1755 {
1756 CBitcoinAddress address;
1757 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
1758 if (wtx.GetDepthInMainChain() >= nMinDepth)
1759 nAmount += txout.nValue;
1760 }
1761 }
1762
1763 return (double)nAmount / (double)COIN;
1764 }
1765
1766
1767 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
1768 {
1769 int64 nBalance = 0;
1770
1771 // Tally wallet transactions
1772 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1773 {
1774 const CWalletTx& wtx = (*it).second;
1775 if (!wtx.IsFinal())
1776 continue;
1777
1778 int64 nGenerated, nReceived, nSent, nFee;
1779 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
1780
1781 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1782 nBalance += nReceived;
1783 nBalance += nGenerated - nSent - nFee;
1784 }
1785
1786 // Tally internal accounting entries
1787 nBalance += walletdb.GetAccountCreditDebit(strAccount);
1788
1789 return nBalance;
1790 }
1791
1792 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
1793 {
1794 CWalletDB walletdb(pwalletMain->strWalletFile);
1795 return GetAccountBalance(walletdb, strAccount, nMinDepth);
1796 }
1797
1798
1799 Value getbalance(const Array& params, bool fHelp)
1800 {
1801 if (fHelp || params.size() > 2)
1802 throw runtime_error(
1803 "getbalance [account] [minconf=1]\n"
1804 "If [account] is not specified, returns the server's total available balance.\n"
1805 "If [account] is specified, returns the balance in the account.");
1806
1807 if (params.size() == 0)
1808 return ValueFromAmount(pwalletMain->GetBalance());
1809
1810 int nMinDepth = 1;
1811 if (params.size() > 1)
1812 nMinDepth = params[1].get_int();
1813
1814 if (params[0].get_str() == "*") {
1815 // Calculate total balance a different way from GetBalance()
1816 // (GetBalance() sums up all unspent TxOuts)
1817 // getbalance and getbalance '*' should always return the same number.
1818 int64 nBalance = 0;
1819 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1820 {
1821 const CWalletTx& wtx = (*it).second;
1822 if (!wtx.IsFinal())
1823 continue;
1824
1825 int64 allGeneratedImmature, allGeneratedMature, allFee;
1826 allGeneratedImmature = allGeneratedMature = allFee = 0;
1827 string strSentAccount;
1828 list<pair<CBitcoinAddress, int64> > listReceived;
1829 list<pair<CBitcoinAddress, int64> > listSent;
1830 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
1831 if (wtx.GetDepthInMainChain() >= nMinDepth)
1832 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
1833 nBalance += r.second;
1834 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
1835 nBalance -= r.second;
1836 nBalance -= allFee;
1837 nBalance += allGeneratedMature;
1838 }
1839 return ValueFromAmount(nBalance);
1840 }
1841
1842 string strAccount = AccountFromValue(params[0]);
1843
1844 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
1845
1846 return ValueFromAmount(nBalance);
1847 }
1848
1849
1850 Value movecmd(const Array& params, bool fHelp)
1851 {
1852 if (fHelp || params.size() < 3 || params.size() > 5)
1853 throw runtime_error(
1854 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
1855 "Move from one account in your wallet to another.");
1856
1857 string strFrom = AccountFromValue(params[0]);
1858 string strTo = AccountFromValue(params[1]);
1859 int64 nAmount = AmountFromValue(params[2]);
1860 if (params.size() > 3)
1861 // unused parameter, used to be nMinDepth, keep type-checking it though
1862 (void)params[3].get_int();
1863 string strComment;
1864 if (params.size() > 4)
1865 strComment = params[4].get_str();
1866
1867 CWalletDB walletdb(pwalletMain->strWalletFile);
1868 walletdb.TxnBegin();
1869
1870 int64 nNow = GetAdjustedTime();
1871
1872 // Debit
1873 CAccountingEntry debit;
1874 debit.strAccount = strFrom;
1875 debit.nCreditDebit = -nAmount;
1876 debit.nTime = nNow;
1877 debit.strOtherAccount = strTo;
1878 debit.strComment = strComment;
1879 walletdb.WriteAccountingEntry(debit);
1880
1881 // Credit
1882 CAccountingEntry credit;
1883 credit.strAccount = strTo;
1884 credit.nCreditDebit = nAmount;
1885 credit.nTime = nNow;
1886 credit.strOtherAccount = strFrom;
1887 credit.strComment = strComment;
1888 walletdb.WriteAccountingEntry(credit);
1889
1890 walletdb.TxnCommit();
1891
1892 return true;
1893 }
1894
1895
1896 Value sendfrom(const Array& params, bool fHelp)
1897 {
1898 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
1899 throw runtime_error(
1900 "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
1901 "<amount> is a real and is rounded to the nearest 0.00000001\n"
1902 "requires wallet passphrase to be set with walletpassphrase first");
1903 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
1904 throw runtime_error(
1905 "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
1906 "<amount> is a real and is rounded to the nearest 0.00000001");
1907
1908 string strAccount = AccountFromValue(params[0]);
1909 CBitcoinAddress address(params[1].get_str());
1910 if (!address.IsValid())
1911 throw JSONRPCError(-5, "Invalid bitcoin address");
1912 int64 nAmount = AmountFromValue(params[2]);
1913 int nMinDepth = 1;
1914 if (params.size() > 3)
1915 nMinDepth = params[3].get_int();
1916
1917 CWalletTx wtx;
1918 wtx.strFromAccount = strAccount;
1919 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
1920 wtx.mapValue["comment"] = params[4].get_str();
1921 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
1922 wtx.mapValue["to"] = params[5].get_str();
1923
1924 if (pwalletMain->IsLocked())
1925 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1926
1927 // Check funds
1928 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
1929 if (nAmount > nBalance)
1930 throw JSONRPCError(-6, "Account has insufficient funds");
1931
1932 // Send
1933 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
1934 if (strError != "")
1935 throw JSONRPCError(-4, strError);
1936
1937 return wtx.GetHash().GetHex();
1938 }
1939
1940
1941 Value sendmany(const Array& params, bool fHelp)
1942 {
1943 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
1944 throw runtime_error(
1945 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
1946 "amounts are double-precision floating point numbers\n"
1947 "requires wallet passphrase to be set with walletpassphrase first");
1948 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
1949 throw runtime_error(
1950 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
1951 "amounts are double-precision floating point numbers");
1952
1953 string strAccount = AccountFromValue(params[0]);
1954 Object sendTo = params[1].get_obj();
1955 int nMinDepth = 1;
1956 if (params.size() > 2)
1957 nMinDepth = params[2].get_int();
1958
1959 CWalletTx wtx;
1960 wtx.strFromAccount = strAccount;
1961 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
1962 wtx.mapValue["comment"] = params[3].get_str();
1963
1964 set<CBitcoinAddress> setAddress;
1965 vector<pair<CScript, int64> > vecSend;
1966
1967 int64 totalAmount = 0;
1968 BOOST_FOREACH(const Pair& s, sendTo)
1969 {
1970 CBitcoinAddress address(s.name_);
1971 if (!address.IsValid())
1972 throw JSONRPCError(-5, string("Invalid bitcoin address:")+s.name_);
1973
1974 if (setAddress.count(address))
1975 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
1976 setAddress.insert(address);
1977
1978 CScript scriptPubKey;
1979 scriptPubKey.SetBitcoinAddress(address);
1980 int64 nAmount = AmountFromValue(s.value_);
1981 totalAmount += nAmount;
1982
1983 vecSend.push_back(make_pair(scriptPubKey, nAmount));
1984 }
1985
1986 if (pwalletMain->IsLocked())
1987 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1988
1989 // Check funds
1990 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
1991 if (totalAmount > nBalance)
1992 throw JSONRPCError(-6, "Account has insufficient funds");
1993
1994 // Send
1995 CReserveKey keyChange(pwalletMain);
1996 int64 nFeeRequired = 0;
1997 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
1998 if (!fCreated)
1999 {
2000 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
2001 throw JSONRPCError(-6, "Insufficient funds");
2002 throw JSONRPCError(-4, "Transaction creation failed");
2003 }
2004 if (!pwalletMain->CommitTransaction(wtx, keyChange))
2005 throw JSONRPCError(-4, "Transaction commit failed");
2006
2007 return wtx.GetHash().GetHex();
2008 }
2009
2010
2011 struct tallyitem
2012 {
2013 int64 nAmount;
2014 int nConf;
2015 tallyitem()
2016 {
2017 nAmount = 0;
2018 nConf = INT_MAX;
2019 }
2020 };
2021
2022 Value ListReceived(const Array& params, bool fByAccounts)
2023 {
2024 // Minimum confirmations
2025 int nMinDepth = 1;
2026 if (params.size() > 0)
2027 nMinDepth = params[0].get_int();
2028
2029 // Whether to include empty accounts
2030 bool fIncludeEmpty = false;
2031 if (params.size() > 1)
2032 fIncludeEmpty = params[1].get_bool();
2033
2034 // Tally
2035 map<CBitcoinAddress, tallyitem> mapTally;
2036 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
2037 {
2038 const CWalletTx& wtx = (*it).second;
2039 if (wtx.IsCoinBase() || !wtx.IsFinal())
2040 continue;
2041
2042 int nDepth = wtx.GetDepthInMainChain();
2043 if (nDepth < nMinDepth)
2044 continue;
2045
2046 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
2047 {
2048 CBitcoinAddress address;
2049 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
2050 continue;
2051
2052 tallyitem& item = mapTally[address];
2053 item.nAmount += txout.nValue;
2054 item.nConf = min(item.nConf, nDepth);
2055 }
2056 }
2057
2058 // Reply
2059 Array ret;
2060 map<string, tallyitem> mapAccountTally;
2061 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2062 {
2063 const CBitcoinAddress& address = item.first;
2064 const string& strAccount = item.second;
2065 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
2066 if (it == mapTally.end() && !fIncludeEmpty)
2067 continue;
2068
2069 int64 nAmount = 0;
2070 int nConf = INT_MAX;
2071 if (it != mapTally.end())
2072 {
2073 nAmount = (*it).second.nAmount;
2074 nConf = (*it).second.nConf;
2075 }
2076
2077 if (fByAccounts)
2078 {
2079 tallyitem& item = mapAccountTally[strAccount];
2080 item.nAmount += nAmount;
2081 item.nConf = min(item.nConf, nConf);
2082 }
2083 else
2084 {
2085 Object obj;
2086 obj.push_back(Pair("address", address.ToString()));
2087 obj.push_back(Pair("account", strAccount));
2088 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
2089 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
2090 ret.push_back(obj);
2091 }
2092 }
2093
2094 if (fByAccounts)
2095 {
2096 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
2097 {
2098 int64 nAmount = (*it).second.nAmount;
2099 int nConf = (*it).second.nConf;
2100 Object obj;
2101 obj.push_back(Pair("account", (*it).first));
2102 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
2103 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
2104 ret.push_back(obj);
2105 }
2106 }
2107
2108 return ret;
2109 }
2110
2111 Value listreceivedbyaddress(const Array& params, bool fHelp)
2112 {
2113 if (fHelp || params.size() > 2)
2114 throw runtime_error(
2115 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
2116 "[minconf] is the minimum number of confirmations before payments are included.\n"
2117 "[includeempty] whether to include addresses that haven't received any payments.\n"
2118 "Returns an array of objects containing:\n"
2119 " \"address\" : receiving address\n"
2120 " \"account\" : the account of the receiving address\n"
2121 " \"amount\" : total amount received by the address\n"
2122 " \"confirmations\" : number of confirmations of the most recent transaction included");
2123
2124 return ListReceived(params, false);
2125 }
2126
2127 Value listreceivedbyaccount(const Array& params, bool fHelp)
2128 {
2129 if (fHelp || params.size() > 2)
2130 throw runtime_error(
2131 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
2132 "[minconf] is the minimum number of confirmations before payments are included.\n"
2133 "[includeempty] whether to include accounts that haven't received any payments.\n"
2134 "Returns an array of objects containing:\n"
2135 " \"account\" : the account of the receiving addresses\n"
2136 " \"amount\" : total amount received by addresses with this account\n"
2137 " \"confirmations\" : number of confirmations of the most recent transaction included");
2138
2139 return ListReceived(params, true);
2140 }
2141
2142 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
2143 {
2144 int64 nGeneratedImmature, nGeneratedMature, nFee;
2145 string strSentAccount;
2146 list<pair<CBitcoinAddress, int64> > listReceived;
2147 list<pair<CBitcoinAddress, int64> > listSent;
2148 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
2149
2150 bool fAllAccounts = (strAccount == string("*"));
2151
2152 // Generated blocks assigned to account ""
2153 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
2154 {
2155 Object entry;
2156 entry.push_back(Pair("account", string("")));
2157 if (nGeneratedImmature)
2158 {
2159 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
2160 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
2161 }
2162 else
2163 {
2164 entry.push_back(Pair("category", "generate"));
2165 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
2166 }
2167 if (fLong)
2168 WalletTxToJSON(wtx, entry);
2169 ret.push_back(entry);
2170 }
2171
2172 // Sent
2173 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
2174 {
2175 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
2176 {
2177 Object entry;
2178 entry.push_back(Pair("account", strSentAccount));
2179 entry.push_back(Pair("address", s.first.ToString()));
2180 entry.push_back(Pair("category", "send"));
2181 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
2182 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
2183 if (fLong)
2184 WalletTxToJSON(wtx, entry);
2185 ret.push_back(entry);
2186 }
2187 }
2188
2189 // Received
2190 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
2191 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
2192 {
2193 string account;
2194 if (pwalletMain->mapAddressBook.count(r.first))
2195 account = pwalletMain->mapAddressBook[r.first];
2196 if (fAllAccounts || (account == strAccount))
2197 {
2198 Object entry;
2199 entry.push_back(Pair("account", account));
2200 entry.push_back(Pair("address", r.first.ToString()));
2201 entry.push_back(Pair("category", "receive"));
2202 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
2203 if (fLong)
2204 WalletTxToJSON(wtx, entry);
2205 ret.push_back(entry);
2206 }
2207 }
2208 }
2209
2210 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
2211 {
2212 bool fAllAccounts = (strAccount == string("*"));
2213
2214 if (fAllAccounts || acentry.strAccount == strAccount)
2215 {
2216 Object entry;
2217 entry.push_back(Pair("account", acentry.strAccount));
2218 entry.push_back(Pair("category", "move"));
2219 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
2220 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
2221 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
2222 entry.push_back(Pair("comment", acentry.strComment));
2223 ret.push_back(entry);
2224 }
2225 }
2226
2227 Value listtransactions(const Array& params, bool fHelp)
2228 {
2229 if (fHelp || params.size() > 3)
2230 throw runtime_error(
2231 "listtransactions [account] [count=10] [from=0]\n"
2232 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
2233
2234 string strAccount = "*";
2235 if (params.size() > 0)
2236 strAccount = params[0].get_str();
2237 int nCount = 10;
2238 if (params.size() > 1)
2239 nCount = params[1].get_int();
2240 int nFrom = 0;
2241 if (params.size() > 2)
2242 nFrom = params[2].get_int();
2243
2244 Array ret;
2245 CWalletDB walletdb(pwalletMain->strWalletFile);
2246
2247 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
2248 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
2249 typedef multimap<int64, TxPair > TxItems;
2250 TxItems txByTime;
2251
2252 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
2253 {
2254 CWalletTx* wtx = &((*it).second);
2255 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
2256 }
2257 list<CAccountingEntry> acentries;
2258 walletdb.ListAccountCreditDebit(strAccount, acentries);
2259 BOOST_FOREACH(CAccountingEntry& entry, acentries)
2260 {
2261 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
2262 }
2263
2264 // Now: iterate backwards until we have nCount items to return:
2265 TxItems::reverse_iterator it = txByTime.rbegin();
2266 if (txByTime.size() > nFrom) std::advance(it, nFrom);
2267 for (; it != txByTime.rend(); ++it)
2268 {
2269 CWalletTx *const pwtx = (*it).second.first;
2270 if (pwtx != 0)
2271 ListTransactions(*pwtx, strAccount, 0, true, ret);
2272 CAccountingEntry *const pacentry = (*it).second.second;
2273 if (pacentry != 0)
2274 AcentryToJSON(*pacentry, strAccount, ret);
2275
2276 if (ret.size() >= nCount) break;
2277 }
2278 // ret is now newest to oldest
2279
2280 // Make sure we return only last nCount items (sends-to-self might give us an extra):
2281 if (ret.size() > nCount)
2282 {
2283 Array::iterator last = ret.begin();
2284 std::advance(last, nCount);
2285 ret.erase(last, ret.end());
2286 }
2287 std::reverse(ret.begin(), ret.end()); // oldest to newest
2288
2289 return ret;
2290 }
2291
2292 Value listaccounts(const Array& params, bool fHelp)
2293 {
2294 if (fHelp || params.size() > 1)
2295 throw runtime_error(
2296 "listaccounts [minconf=1]\n"
2297 "Returns Object that has account names as keys, account balances as values.");
2298
2299 int nMinDepth = 1;
2300 if (params.size() > 0)
2301 nMinDepth = params[0].get_int();
2302
2303 map<string, int64> mapAccountBalances;
2304 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
2305 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
2306 mapAccountBalances[entry.second] = 0;
2307 }
2308
2309 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
2310 {
2311 const CWalletTx& wtx = (*it).second;
2312 int64 nGeneratedImmature, nGeneratedMature, nFee;
2313 string strSentAccount;
2314 list<pair<CBitcoinAddress, int64> > listReceived;
2315 list<pair<CBitcoinAddress, int64> > listSent;
2316 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
2317 mapAccountBalances[strSentAccount] -= nFee;
2318 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
2319 mapAccountBalances[strSentAccount] -= s.second;
2320 if (wtx.GetDepthInMainChain() >= nMinDepth)
2321 {
2322 mapAccountBalances[""] += nGeneratedMature;
2323 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
2324 if (pwalletMain->mapAddressBook.count(r.first))
2325 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
2326 else
2327 mapAccountBalances[""] += r.second;
2328 }
2329 }
2330
2331 list<CAccountingEntry> acentries;
2332 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
2333 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
2334 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
2335
2336 Object ret;
2337 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
2338 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
2339 }
2340 return ret;
2341 }
2342
2343 Value listsinceblock(const Array& params, bool fHelp)
2344 {
2345 if (fHelp)
2346 throw runtime_error(
2347 "listsinceblock [blockid] [target-confirmations]\n"
2348 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
2349
2350 CBlockIndex *pindex = NULL;
2351 int target_confirms = 1;
2352
2353 if (params.size() > 0)
2354 {
2355 uint256 blockId = 0;
2356
2357 blockId.SetHex(params[0].get_str());
2358 pindex = CBlockLocator(blockId).GetBlockIndex();
2359 }
2360
2361 if (params.size() > 1)
2362 {
2363 target_confirms = params[1].get_int();
2364
2365 if (target_confirms < 1)
2366 throw JSONRPCError(-8, "Invalid parameter");
2367 }
2368
2369 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
2370
2371 Array transactions;
2372
2373 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
2374 {
2375 CWalletTx tx = (*it).second;
2376
2377 if (depth == -1 || tx.GetDepthInMainChain() < depth)
2378 ListTransactions(tx, "*", 0, true, transactions);
2379 }
2380
2381 uint256 lastblock;
2382
2383 if (target_confirms == 1)
2384 {
2385 printf("oops!\n");
2386 lastblock = hashBestChain;
2387 }
2388 else
2389 {
2390 int target_height = pindexBest->nHeight + 1 - target_confirms;
2391
2392 CBlockIndex *block;
2393 for (block = pindexBest;
2394 block && block->nHeight > target_height;
2395 block = block->pprev) { }
2396
2397 lastblock = block ? block->GetBlockHash() : 0;
2398 }
2399
2400 Object ret;
2401 ret.push_back(Pair("transactions", transactions));
2402 ret.push_back(Pair("lastblock", lastblock.GetHex()));
2403
2404 return ret;
2405 }
2406
2407 Value gettransaction(const Array& params, bool fHelp)
2408 {
2409 if (fHelp || params.size() != 1)
2410 throw runtime_error(
2411 "gettransaction <txid>\n"
2412 "Get detailed information about <txid>");
2413
2414 uint256 hash;
2415 hash.SetHex(params[0].get_str());
2416
2417 Object entry;
2418
2419 if (!pwalletMain->mapWallet.count(hash))
2420 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
2421 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
2422
2423 int64 nCredit = wtx.GetCredit();
2424 int64 nDebit = wtx.GetDebit();
2425 int64 nNet = nCredit - nDebit;
2426 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
2427
2428 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
2429 if (wtx.IsFromMe())
2430 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
2431
2432 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
2433
2434 Array details;
2435 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
2436 entry.push_back(Pair("details", details));
2437
2438 return entry;
2439 }
2440
2441
2442 Value backupwallet(const Array& params, bool fHelp)
2443 {
2444 if (fHelp || params.size() != 1)
2445 throw runtime_error(
2446 "backupwallet <destination>\n"
2447 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
2448
2449 string strDest = params[0].get_str();
2450 BackupWallet(*pwalletMain, strDest);
2451
2452 return Value::null;
2453 }
2454
2455
2456 Value keypoolrefill(const Array& params, bool fHelp)
2457 {
2458 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
2459 throw runtime_error(
2460 "keypoolrefill\n"
2461 "Fills the keypool, requires wallet passphrase to be set.");
2462 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
2463 throw runtime_error(
2464 "keypoolrefill\n"
2465 "Fills the keypool.");
2466
2467 if (pwalletMain->IsLocked())
2468 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
2469
2470 pwalletMain->TopUpKeyPool();
2471
2472 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
2473 throw JSONRPCError(-4, "Error refreshing keypool.");
2474
2475 return Value::null;
2476 }
2477
2478
2479 void ThreadTopUpKeyPool(void* parg)
2480 {
2481 pwalletMain->TopUpKeyPool();
2482 }
2483
2484 void ThreadCleanWalletPassphrase(void* parg)
2485 {
2486 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
2487
2488 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
2489
2490 if (nWalletUnlockTime == 0)
2491 {
2492 nWalletUnlockTime = nMyWakeTime;
2493
2494 do
2495 {
2496 if (nWalletUnlockTime==0)
2497 break;
2498 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
2499 if (nToSleep <= 0)
2500 break;
2501
2502 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
2503 Sleep(nToSleep);
2504 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
2505
2506 } while(1);
2507
2508 if (nWalletUnlockTime)
2509 {
2510 nWalletUnlockTime = 0;
2511 pwalletMain->Lock();
2512 }
2513 }
2514 else
2515 {
2516 if (nWalletUnlockTime < nMyWakeTime)
2517 nWalletUnlockTime = nMyWakeTime;
2518 }
2519
2520 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
2521
2522 delete (int64*)parg;
2523 }
2524
2525 Value walletpassphrase(const Array& params, bool fHelp)
2526 {
2527 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
2528 throw runtime_error(
2529 "walletpassphrase <passphrase> <timeout>\n"
2530 "Stores the wallet decryption key in memory for <timeout> seconds.");
2531 if (fHelp)
2532 return true;
2533 if (!pwalletMain->IsCrypted())
2534 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
2535
2536 if (!pwalletMain->IsLocked())
2537 throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
2538
2539 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
2540 SecureString strWalletPass;
2541 strWalletPass.reserve(100);
2542 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
2543 // Alternately, find a way to make params[0] mlock()'d to begin with.
2544 strWalletPass = params[0].get_str().c_str();
2545
2546 if (strWalletPass.length() > 0)
2547 {
2548 if (!pwalletMain->Unlock(strWalletPass))
2549 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
2550 }
2551 else
2552 throw runtime_error(
2553 "walletpassphrase <passphrase> <timeout>\n"
2554 "Stores the wallet decryption key in memory for <timeout> seconds.");
2555
2556 CreateThread(ThreadTopUpKeyPool, NULL);
2557 int64* pnSleepTime = new int64(params[1].get_int64());
2558 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
2559
2560 return Value::null;
2561 }
2562
2563
2564 Value walletpassphrasechange(const Array& params, bool fHelp)
2565 {
2566 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
2567 throw runtime_error(
2568 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
2569 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
2570 if (fHelp)
2571 return true;
2572 if (!pwalletMain->IsCrypted())
2573 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
2574
2575 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
2576 // Alternately, find a way to make params[0] mlock()'d to begin with.
2577 SecureString strOldWalletPass;
2578 strOldWalletPass.reserve(100);
2579 strOldWalletPass = params[0].get_str().c_str();
2580
2581 SecureString strNewWalletPass;
2582 strNewWalletPass.reserve(100);
2583 strNewWalletPass = params[1].get_str().c_str();
2584
2585 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
2586 throw runtime_error(
2587 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
2588 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
2589
2590 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
2591 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
2592
2593 return Value::null;
2594 }
2595
2596
2597 Value walletlock(const Array& params, bool fHelp)
2598 {
2599 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
2600 throw runtime_error(
2601 "walletlock\n"
2602 "Removes the wallet encryption key from memory, locking the wallet.\n"
2603 "After calling this method, you will need to call walletpassphrase again\n"
2604 "before being able to call any methods which require the wallet to be unlocked.");
2605 if (fHelp)
2606 return true;
2607 if (!pwalletMain->IsCrypted())
2608 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
2609
2610 CRITICAL_BLOCK(cs_nWalletUnlockTime)
2611 {
2612 pwalletMain->Lock();
2613 nWalletUnlockTime = 0;
2614 }
2615
2616 return Value::null;
2617 }
2618
2619
2620 Value encryptwallet(const Array& params, bool fHelp)
2621 {
2622 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
2623 throw runtime_error(
2624 "encryptwallet <passphrase>\n"
2625 "Encrypts the wallet with <passphrase>.");
2626 if (fHelp)
2627 return true;
2628 if (pwalletMain->IsCrypted())
2629 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
2630
2631 #ifdef QT_GUI
2632 // shutting down via RPC while the GUI is running does not work (yet):
2633 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
2634 #endif
2635
2636 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
2637 // Alternately, find a way to make params[0] mlock()'d to begin with.
2638 SecureString strWalletPass;
2639 strWalletPass.reserve(100);
2640 strWalletPass = params[0].get_str().c_str();
2641
2642 if (strWalletPass.length() < 1)
2643 throw runtime_error(
2644 "encryptwallet <passphrase>\n"
2645 "Encrypts the wallet with <passphrase>.");
2646
2647 if (!pwalletMain->EncryptWallet(strWalletPass))
2648 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
2649
2650 // BDB seems to have a bad habit of writing old data into
2651 // slack space in .dat files; that is bad if the old data is
2652 // unencrypted private keys. So:
2653 CreateThread(Shutdown, NULL);
2654 return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet";
2655 }
2656
2657
2658 Value validateaddress(const Array& params, bool fHelp)
2659 {
2660 if (fHelp || params.size() != 1)
2661 throw runtime_error(
2662 "validateaddress <bitcoinaddress>\n"
2663 "Return information about <bitcoinaddress>.");
2664
2665 CBitcoinAddress address(params[0].get_str());
2666 bool isValid = address.IsValid();
2667
2668 Object ret;
2669 ret.push_back(Pair("isvalid", isValid));
2670 if (isValid)
2671 {
2672 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
2673 // version of the address:
2674 string currentAddress = address.ToString();
2675 ret.push_back(Pair("address", currentAddress));
2676 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
2677 if (pwalletMain->mapAddressBook.count(address))
2678 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
2679 }
2680 return ret;
2681 }
2682
2683
2684 Value getwork(const Array& params, bool fHelp)
2685 {
2686 if (fHelp || params.size() > 1)
2687 throw runtime_error(
2688 "getwork [data]\n"
2689 "If [data] is not specified, returns formatted hash data to work on:\n"
2690 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
2691 " \"data\" : block data\n"
2692 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
2693 " \"target\" : little endian hash target\n"
2694 "If [data] is specified, tries to solve the block and returns true if it was successful.");
2695
2696 if (vNodes.empty())
2697 throw JSONRPCError(-9, "Bitcoin is not connected!");
2698
2699 if (IsInitialBlockDownload())
2700 throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
2701
2702 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
2703 static mapNewBlock_t mapNewBlock;
2704 static vector<CBlock*> vNewBlock;
2705 static CReserveKey reservekey(pwalletMain);
2706
2707 if (params.size() == 0)
2708 {
2709 // Update block
2710 static unsigned int nTransactionsUpdatedLast;
2711 static CBlockIndex* pindexPrev;
2712 static int64 nStart;
2713 static CBlock* pblock;
2714 if (pindexPrev != pindexBest ||
2715 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
2716 {
2717 if (pindexPrev != pindexBest)
2718 {
2719 // Deallocate old blocks since they're obsolete now
2720 mapNewBlock.clear();
2721 BOOST_FOREACH(CBlock* pblock, vNewBlock)
2722 delete pblock;
2723 vNewBlock.clear();
2724 }
2725 nTransactionsUpdatedLast = nTransactionsUpdated;
2726 pindexPrev = pindexBest;
2727 nStart = GetTime();
2728
2729 // Create new block
2730 pblock = CreateNewBlock(reservekey);
2731 if (!pblock)
2732 throw JSONRPCError(-7, "Out of memory");
2733 vNewBlock.push_back(pblock);
2734 }
2735
2736 // Update nTime
2737 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
2738 pblock->nNonce = 0;
2739
2740 // Update nExtraNonce
2741 static unsigned int nExtraNonce = 0;
2742 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
2743
2744 // Save
2745 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
2746
2747 // Prebuild hash buffers
2748 char pmidstate[32];
2749 char pdata[128];
2750 char phash1[64];
2751 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
2752
2753 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
2754
2755 Object result;
2756 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
2757 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
2758 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
2759 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
2760 return result;
2761 }
2762 else
2763 {
2764 // Parse parameters
2765 vector<unsigned char> vchData = ParseHex(params[0].get_str());
2766 if (vchData.size() != 128)
2767 throw JSONRPCError(-8, "Invalid parameter");
2768 CBlock* pdata = (CBlock*)&vchData[0];
2769
2770 // Byte reverse
2771 for (int i = 0; i < 128/4; i++)
2772 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
2773
2774 // Get saved block
2775 if (!mapNewBlock.count(pdata->hashMerkleRoot))
2776 return false;
2777 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
2778
2779 pblock->nTime = pdata->nTime;
2780 pblock->nNonce = pdata->nNonce;
2781 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
2782 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
2783
2784 return CheckWork(pblock, *pwalletMain, reservekey);
2785 }
2786 }
2787
2788
2789 Value getmemorypool(const Array& params, bool fHelp)
2790 {
2791 if (fHelp || params.size() > 1)
2792 throw runtime_error(
2793 "getmemorypool [data]\n"
2794 "If [data] is not specified, returns data needed to construct a block to work on:\n"
2795 " \"version\" : block version\n"
2796 " \"previousblockhash\" : hash of current highest block\n"
2797 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
2798 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
2799 " \"time\" : timestamp appropriate for next block\n"
2800 " \"bits\" : compressed target of next block\n"
2801 "If [data] is specified, tries to solve the block and returns true if it was successful.");
2802
2803 if (params.size() == 0)
2804 {
2805 if (vNodes.empty())
2806 throw JSONRPCError(-9, "Bitcoin is not connected!");
2807
2808 if (IsInitialBlockDownload())
2809 throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
2810
2811 static CReserveKey reservekey(pwalletMain);
2812
2813 // Update block
2814 static unsigned int nTransactionsUpdatedLast;
2815 static CBlockIndex* pindexPrev;
2816 static int64 nStart;
2817 static CBlock* pblock;
2818 if (pindexPrev != pindexBest ||
2819 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
2820 {
2821 nTransactionsUpdatedLast = nTransactionsUpdated;
2822 pindexPrev = pindexBest;
2823 nStart = GetTime();
2824
2825 // Create new block
2826 if(pblock)
2827 delete pblock;
2828 pblock = CreateNewBlock(reservekey);
2829 if (!pblock)
2830 throw JSONRPCError(-7, "Out of memory");
2831 }
2832
2833 // Update nTime
2834 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
2835 pblock->nNonce = 0;
2836
2837 Array transactions;
2838 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
2839 if(tx.IsCoinBase())
2840 continue;
2841
2842 CDataStream ssTx;
2843 ssTx << tx;
2844
2845 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
2846 }
2847
2848 Object result;
2849 result.push_back(Pair("version", pblock->nVersion));
2850 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
2851 result.push_back(Pair("transactions", transactions));
2852 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
2853 result.push_back(Pair("time", (int64_t)pblock->nTime));
2854
2855 union {
2856 int32_t nBits;
2857 char cBits[4];
2858 } uBits;
2859 uBits.nBits = htonl((int32_t)pblock->nBits);
2860 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
2861
2862 return result;
2863 }
2864 else
2865 {
2866 // Parse parameters
2867 CDataStream ssBlock(ParseHex(params[0].get_str()));
2868 CBlock pblock;
2869 ssBlock >> pblock;
2870
2871 return ProcessBlock(NULL, &pblock);
2872 }
2873 }
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885 //
2886 // Call Table
2887 //
2888
2889 pair<string, rpcfn_type> pCallTable[] =
2890 {
2891 make_pair("help", &help),
2892 make_pair("stop", &stop),
2893 make_pair("getblockcount", &getblockcount),
2894 make_pair("getblocknumber", &getblocknumber),
2895 make_pair("getconnectioncount", &getconnectioncount),
2896 make_pair("getdifficulty", &getdifficulty),
2897 make_pair("getgenerate", &getgenerate),
2898 make_pair("setgenerate", &setgenerate),
2899 make_pair("gethashespersec", &gethashespersec),
2900 make_pair("getinfo", &getinfo),
2901 make_pair("getnewaddress", &getnewaddress),
2902 make_pair("getaccountaddress", &getaccountaddress),
2903 make_pair("setaccount", &setaccount),
2904 make_pair("getaccount", &getaccount),
2905 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2906 make_pair("sendtoaddress", &sendtoaddress),
2907 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2908 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2909 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2910 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2911 make_pair("backupwallet", &backupwallet),
2912 make_pair("keypoolrefill", &keypoolrefill),
2913 make_pair("walletpassphrase", &walletpassphrase),
2914 make_pair("walletpassphrasechange", &walletpassphrasechange),
2915 make_pair("walletlock", &walletlock),
2916 make_pair("encryptwallet", &encryptwallet),
2917 make_pair("validateaddress", &validateaddress),
2918 make_pair("getbalance", &getbalance),
2919 make_pair("move", &movecmd),
2920 make_pair("sendfrom", &sendfrom),
2921 make_pair("sendmany", &sendmany),
2922 make_pair("gettransaction", &gettransaction),
2923 make_pair("listtransactions", &listtransactions),
2924 make_pair("signmessage", &signmessage),
2925 make_pair("verifymessage", &verifymessage),
2926 make_pair("getwork", &getwork),
2927 make_pair("listaccounts", &listaccounts),
2928 make_pair("settxfee", &settxfee),
2929 make_pair("getmemorypool", &getmemorypool),
2930 make_pair("listsinceblock", &listsinceblock),
2931 };
2932 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2933
2934 string pAllowInSafeMode[] =
2935 {
2936 "help",
2937 "stop",
2938 "getblockcount",
2939 "getblocknumber", // deprecated
2940 "getconnectioncount",
2941 "getdifficulty",
2942 "getgenerate",
2943 "setgenerate",
2944 "gethashespersec",
2945 "getinfo",
2946 "getnewaddress",
2947 "getaccountaddress",
2948 "getaccount",
2949 "getaddressesbyaccount",
2950 "backupwallet",
2951 "keypoolrefill",
2952 "walletpassphrase",
2953 "walletlock",
2954 "validateaddress",
2955 "getwork",
2956 "getmemorypool",
2957 };
2958 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2959
2960
2961
2962
2963 //
2964 // HTTP protocol
2965 //
2966 // This ain't Apache. We're just using HTTP header for the length field
2967 // and to be compatible with other JSON-RPC implementations.
2968 //
2969
2970 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2971 {
2972 ostringstream s;
2973 s << "POST / HTTP/1.1\r\n"
2974 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2975 << "Host: 127.0.0.1\r\n"
2976 << "Content-Type: application/json\r\n"
2977 << "Content-Length: " << strMsg.size() << "\r\n"
2978 << "Connection: close\r\n"
2979 << "Accept: application/json\r\n";
2980 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2981 s << item.first << ": " << item.second << "\r\n";
2982 s << "\r\n" << strMsg;
2983
2984 return s.str();
2985 }
2986
2987 string rfc1123Time()
2988 {
2989 char buffer[64];
2990 time_t now;
2991 time(&now);
2992 struct tm* now_gmt = gmtime(&now);
2993 string locale(setlocale(LC_TIME, NULL));
2994 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2995 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2996 setlocale(LC_TIME, locale.c_str());
2997 return string(buffer);
2998 }
2999
3000 static string HTTPReply(int nStatus, const string& strMsg)
3001 {
3002 if (nStatus == 401)
3003 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
3004 "Date: %s\r\n"
3005 "Server: bitcoin-json-rpc/%s\r\n"
3006 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
3007 "Content-Type: text/html\r\n"
3008 "Content-Length: 296\r\n"
3009 "\r\n"
3010 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
3011 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
3012 "<HTML>\r\n"
3013 "<HEAD>\r\n"
3014 "<TITLE>Error</TITLE>\r\n"
3015 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
3016 "</HEAD>\r\n"
3017 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
3018 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
3019 const char *cStatus;
3020 if (nStatus == 200) cStatus = "OK";
3021 else if (nStatus == 400) cStatus = "Bad Request";
3022 else if (nStatus == 403) cStatus = "Forbidden";
3023 else if (nStatus == 404) cStatus = "Not Found";
3024 else if (nStatus == 500) cStatus = "Internal Server Error";
3025 else cStatus = "";
3026 return strprintf(
3027 "HTTP/1.1 %d %s\r\n"
3028 "Date: %s\r\n"
3029 "Connection: close\r\n"
3030 "Content-Length: %d\r\n"
3031 "Content-Type: application/json\r\n"
3032 "Server: bitcoin-json-rpc/%s\r\n"
3033 "\r\n"
3034 "%s",
3035 nStatus,
3036 cStatus,
3037 rfc1123Time().c_str(),
3038 strMsg.size(),
3039 FormatFullVersion().c_str(),
3040 strMsg.c_str());
3041 }
3042
3043 int ReadHTTPStatus(std::basic_istream<char>& stream)
3044 {
3045 string str;
3046 getline(stream, str);
3047 vector<string> vWords;
3048 boost::split(vWords, str, boost::is_any_of(" "));
3049 if (vWords.size() < 2)
3050 return 500;
3051 return atoi(vWords[1].c_str());
3052 }
3053
3054 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
3055 {
3056 int nLen = 0;
3057 loop
3058 {
3059 string str;
3060 std::getline(stream, str);
3061 if (str.empty() || str == "\r")
3062 break;
3063 string::size_type nColon = str.find(":");
3064 if (nColon != string::npos)
3065 {
3066 string strHeader = str.substr(0, nColon);
3067 boost::trim(strHeader);
3068 boost::to_lower(strHeader);
3069 string strValue = str.substr(nColon+1);
3070 boost::trim(strValue);
3071 mapHeadersRet[strHeader] = strValue;
3072 if (strHeader == "content-length")
3073 nLen = atoi(strValue.c_str());
3074 }
3075 }
3076 return nLen;
3077 }
3078
3079 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
3080 {
3081 mapHeadersRet.clear();
3082 strMessageRet = "";
3083
3084 // Read status
3085 int nStatus = ReadHTTPStatus(stream);
3086
3087 // Read header
3088 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
3089 if (nLen < 0 || nLen > MAX_SIZE)
3090 return 500;
3091
3092 // Read message
3093 if (nLen > 0)
3094 {
3095 vector<char> vch(nLen);
3096 stream.read(&vch[0], nLen);
3097 strMessageRet = string(vch.begin(), vch.end());
3098 }
3099
3100 return nStatus;
3101 }
3102
3103 bool HTTPAuthorized(map<string, string>& mapHeaders)
3104 {
3105 string strAuth = mapHeaders["authorization"];
3106 if (strAuth.substr(0,6) != "Basic ")
3107 return false;
3108 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
3109 string strUserPass = DecodeBase64(strUserPass64);
3110 return strUserPass == strRPCUserColonPass;
3111 }
3112
3113 //
3114 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
3115 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
3116 // unspecified (HTTP errors and contents of 'error').
3117 //
3118 // 1.0 spec: http://json-rpc.org/wiki/specification
3119 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
3120 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
3121 //
3122
3123 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
3124 {
3125 Object request;
3126 request.push_back(Pair("method", strMethod));
3127 request.push_back(Pair("params", params));
3128 request.push_back(Pair("id", id));
3129 return write_string(Value(request), false) + "\n";
3130 }
3131
3132 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
3133 {
3134 Object reply;
3135 if (error.type() != null_type)
3136 reply.push_back(Pair("result", Value::null));
3137 else
3138 reply.push_back(Pair("result", result));
3139 reply.push_back(Pair("error", error));
3140 reply.push_back(Pair("id", id));
3141 return write_string(Value(reply), false) + "\n";
3142 }
3143
3144 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
3145 {
3146 // Send error reply from json-rpc error object
3147 int nStatus = 500;
3148 int code = find_value(objError, "code").get_int();
3149 if (code == -32600) nStatus = 400;
3150 else if (code == -32601) nStatus = 404;
3151 string strReply = JSONRPCReply(Value::null, objError, id);
3152 stream << HTTPReply(nStatus, strReply) << std::flush;
3153 }
3154
3155 bool ClientAllowed(const string& strAddress)
3156 {
3157 if (strAddress == asio::ip::address_v4::loopback().to_string())
3158 return true;
3159 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
3160 BOOST_FOREACH(string strAllow, vAllow)
3161 if (WildcardMatch(strAddress, strAllow))
3162 return true;
3163 return false;
3164 }
3165
3166 #ifdef USE_SSL
3167 //
3168 // IOStream device that speaks SSL but can also speak non-SSL
3169 //
3170 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
3171 public:
3172 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
3173 {
3174 fUseSSL = fUseSSLIn;
3175 fNeedHandshake = fUseSSLIn;
3176 }
3177
3178 void handshake(ssl::stream_base::handshake_type role)
3179 {
3180 if (!fNeedHandshake) return;
3181 fNeedHandshake = false;
3182 stream.handshake(role);
3183 }
3184 std::streamsize read(char* s, std::streamsize n)
3185 {
3186 handshake(ssl::stream_base::server); // HTTPS servers read first
3187 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
3188 return stream.next_layer().read_some(asio::buffer(s, n));
3189 }
3190 std::streamsize write(const char* s, std::streamsize n)
3191 {
3192 handshake(ssl::stream_base::client); // HTTPS clients write first
3193 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
3194 return asio::write(stream.next_layer(), asio::buffer(s, n));
3195 }
3196 bool connect(const std::string& server, const std::string& port)
3197 {
3198 ip::tcp::resolver resolver(stream.get_io_service());
3199 ip::tcp::resolver::query query(server.c_str(), port.c_str());
3200 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
3201 ip::tcp::resolver::iterator end;
3202 boost::system::error_code error = asio::error::host_not_found;
3203 while (error && endpoint_iterator != end)
3204 {
3205 stream.lowest_layer().close();
3206 stream.lowest_layer().connect(*endpoint_iterator++, error);
3207 }
3208 if (error)
3209 return false;
3210 return true;
3211 }
3212
3213 private:
3214 bool fNeedHandshake;
3215 bool fUseSSL;
3216 SSLStream& stream;
3217 };
3218 #endif
3219
3220 void ThreadRPCServer(void* parg)
3221 {
3222 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
3223 try
3224 {
3225 vnThreadsRunning[4]++;
3226 ThreadRPCServer2(parg);
3227 vnThreadsRunning[4]--;
3228 }
3229 catch (std::exception& e) {
3230 vnThreadsRunning[4]--;
3231 PrintException(&e, "ThreadRPCServer()");
3232 } catch (...) {
3233 vnThreadsRunning[4]--;
3234 PrintException(NULL, "ThreadRPCServer()");
3235 }
3236 printf("ThreadRPCServer exiting\n");
3237 }
3238
3239 void ThreadRPCServer2(void* parg)
3240 {
3241 printf("ThreadRPCServer started\n");
3242
3243 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
3244 if (strRPCUserColonPass == ":")
3245 {
3246 unsigned char rand_pwd[32];
3247 RAND_bytes(rand_pwd, 32);
3248 string strWhatAmI = "To use bitcoind";
3249 if (mapArgs.count("-server"))
3250 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
3251 else if (mapArgs.count("-daemon"))
3252 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
3253 PrintConsole(
3254 _("Error: %s, you must set a rpcpassword in the configuration file:\n %s\n"
3255 "It is recommended you use the following random password:\n"
3256 "rpcuser=bitcoinrpc\n"
3257 "rpcpassword=%s\n"
3258 "(you do not need to remember this password)\n"
3259 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
3260 strWhatAmI.c_str(),
3261 GetConfigFile().c_str(),
3262 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str());
3263 #ifndef QT_GUI
3264 CreateThread(Shutdown, NULL);
3265 #endif
3266 return;
3267 }
3268
3269 bool fUseSSL = GetBoolArg("-rpcssl");
3270 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
3271
3272 asio::io_service io_service;
3273 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
3274 ip::tcp::acceptor acceptor(io_service, endpoint);
3275
3276 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
3277
3278 #ifdef USE_SSL
3279 ssl::context context(io_service, ssl::context::sslv23);
3280 if (fUseSSL)
3281 {
3282 context.set_options(ssl::context::no_sslv2);
3283 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
3284 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
3285 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
3286 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
3287 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
3288 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
3289 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
3290 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
3291
3292 string ciphers = GetArg("-rpcsslciphers",
3293 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
3294 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
3295 }
3296 #else
3297 if (fUseSSL)
3298 throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
3299 #endif
3300
3301 loop
3302 {
3303 // Accept connection
3304 #ifdef USE_SSL
3305 SSLStream sslStream(io_service, context);
3306 SSLIOStreamDevice d(sslStream, fUseSSL);
3307 iostreams::stream<SSLIOStreamDevice> stream(d);
3308 #else
3309 ip::tcp::iostream stream;
3310 #endif
3311
3312 ip::tcp::endpoint peer;
3313 vnThreadsRunning[4]--;
3314 #ifdef USE_SSL
3315 acceptor.accept(sslStream.lowest_layer(), peer);
3316 #else
3317 acceptor.accept(*stream.rdbuf(), peer);
3318 #endif
3319 vnThreadsRunning[4]++;
3320 if (fShutdown)
3321 return;
3322
3323 // Restrict callers by IP
3324 if (!ClientAllowed(peer.address().to_string()))
3325 {
3326 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
3327 if (!fUseSSL)
3328 stream << HTTPReply(403, "") << std::flush;
3329 continue;
3330 }
3331
3332 map<string, string> mapHeaders;
3333 string strRequest;
3334
3335 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
3336 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
3337 { // Timed out:
3338 acceptor.cancel();
3339 printf("ThreadRPCServer ReadHTTP timeout\n");
3340 continue;
3341 }
3342
3343 // Check authorization
3344 if (mapHeaders.count("authorization") == 0)
3345 {
3346 stream << HTTPReply(401, "") << std::flush;
3347 continue;
3348 }
3349 if (!HTTPAuthorized(mapHeaders))
3350 {
3351 printf("ThreadRPCServer incorrect password attempt from %s\n",peer.address().to_string().c_str());
3352 /* Deter brute-forcing short passwords.
3353 If this results in a DOS the user really
3354 shouldn't have their RPC port exposed.*/
3355 if (mapArgs["-rpcpassword"].size() < 20)
3356 Sleep(250);
3357
3358 stream << HTTPReply(401, "") << std::flush;
3359 continue;
3360 }
3361
3362 Value id = Value::null;
3363 try
3364 {
3365 // Parse request
3366 Value valRequest;
3367 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
3368 throw JSONRPCError(-32700, "Parse error");
3369 const Object& request = valRequest.get_obj();
3370
3371 // Parse id now so errors from here on will have the id
3372 id = find_value(request, "id");
3373
3374 // Parse method
3375 Value valMethod = find_value(request, "method");
3376 if (valMethod.type() == null_type)
3377 throw JSONRPCError(-32600, "Missing method");
3378 if (valMethod.type() != str_type)
3379 throw JSONRPCError(-32600, "Method must be a string");
3380 string strMethod = valMethod.get_str();
3381 if (strMethod != "getwork" && strMethod != "getmemorypool")
3382 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
3383
3384 // Parse params
3385 Value valParams = find_value(request, "params");
3386 Array params;
3387 if (valParams.type() == array_type)
3388 params = valParams.get_array();
3389 else if (valParams.type() == null_type)
3390 params = Array();
3391 else
3392 throw JSONRPCError(-32600, "Params must be an array");
3393
3394 // Find method
3395 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
3396 if (mi == mapCallTable.end())
3397 throw JSONRPCError(-32601, "Method not found");
3398
3399 // Observe safe mode
3400 string strWarning = GetWarnings("rpc");
3401 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
3402 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
3403
3404 try
3405 {
3406 // Execute
3407 Value result;
3408 CRITICAL_BLOCK(cs_main)
3409 CRITICAL_BLOCK(pwalletMain->cs_wallet)
3410 result = (*(*mi).second)(params, false);
3411
3412 // Send reply
3413 string strReply = JSONRPCReply(result, Value::null, id);
3414 stream << HTTPReply(200, strReply) << std::flush;
3415 }
3416 catch (std::exception& e)
3417 {
3418 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
3419 }
3420 }
3421 catch (Object& objError)
3422 {
3423 ErrorReply(stream, objError, id);
3424 }
3425 catch (std::exception& e)
3426 {
3427 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
3428 }
3429 }
3430 }
3431
3432
3433
3434
3435 Object CallRPC(const string& strMethod, const Array& params)
3436 {
3437 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
3438 throw runtime_error(strprintf(
3439 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
3440 "If the file does not exist, create it with owner-readable-only file permissions."),
3441 GetConfigFile().c_str()));
3442
3443 // Connect to localhost
3444 bool fUseSSL = GetBoolArg("-rpcssl");
3445 #ifdef USE_SSL
3446 asio::io_service io_service;
3447 ssl::context context(io_service, ssl::context::sslv23);
3448 context.set_options(ssl::context::no_sslv2);
3449 SSLStream sslStream(io_service, context);
3450 SSLIOStreamDevice d(sslStream, fUseSSL);
3451 iostreams::stream<SSLIOStreamDevice> stream(d);
3452 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
3453 throw runtime_error("couldn't connect to server");
3454 #else
3455 if (fUseSSL)
3456 throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
3457
3458 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
3459 if (stream.fail())
3460 throw runtime_error("couldn't connect to server");
3461 #endif
3462
3463
3464 // HTTP basic authentication
3465 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
3466 map<string, string> mapRequestHeaders;
3467 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
3468
3469 // Send request
3470 string strRequest = JSONRPCRequest(strMethod, params, 1);
3471 string strPost = HTTPPost(strRequest, mapRequestHeaders);
3472 stream << strPost << std::flush;
3473
3474 // Receive reply
3475 map<string, string> mapHeaders;
3476 string strReply;
3477 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
3478 if (nStatus == 401)
3479 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
3480 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
3481 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
3482 else if (strReply.empty())
3483 throw runtime_error("no response from server");
3484
3485 // Parse reply
3486 Value valReply;
3487 if (!read_string(strReply, valReply))
3488 throw runtime_error("couldn't parse reply from server");
3489 const Object& reply = valReply.get_obj();
3490 if (reply.empty())
3491 throw runtime_error("expected reply to have result, error and id properties");
3492
3493 return reply;
3494 }
3495
3496
3497
3498
3499 template<typename T>
3500 void ConvertTo(Value& value)
3501 {
3502 if (value.type() == str_type)
3503 {
3504 // reinterpret string as unquoted json value
3505 Value value2;
3506 if (!read_string(value.get_str(), value2))
3507 throw runtime_error("type mismatch");
3508 value = value2.get_value<T>();
3509 }
3510 else
3511 {
3512 value = value.get_value<T>();
3513 }
3514 }
3515
3516 int CommandLineRPC(int argc, char *argv[])
3517 {
3518 string strPrint;
3519 int nRet = 0;
3520 try
3521 {
3522 // Skip switches
3523 while (argc > 1 && IsSwitchChar(argv[1][0]))
3524 {
3525 argc--;
3526 argv++;
3527 }
3528
3529 // Method
3530 if (argc < 2)
3531 throw runtime_error("too few parameters");
3532 string strMethod = argv[1];
3533
3534 // Parameters default to strings
3535 Array params;
3536 for (int i = 2; i < argc; i++)
3537 params.push_back(argv[i]);
3538 int n = params.size();
3539
3540 //
3541 // Special case non-string parameter types
3542 //
3543 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
3544 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3545 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
3546 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
3547 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3548 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3549 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
3550 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
3551 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
3552 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
3553 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3554 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
3555 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
3556 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
3557 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
3558 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3559 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
3560 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
3561 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3562 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
3563 if (strMethod == "sendmany" && n > 1)
3564 {
3565 string s = params[1].get_str();
3566 Value v;
3567 if (!read_string(s, v) || v.type() != obj_type)
3568 throw runtime_error("type mismatch");
3569 params[1] = v.get_obj();
3570 }
3571 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
3572
3573 // Execute
3574 Object reply = CallRPC(strMethod, params);
3575
3576 // Parse reply
3577 const Value& result = find_value(reply, "result");
3578 const Value& error = find_value(reply, "error");
3579
3580 if (error.type() != null_type)
3581 {
3582 // Error
3583 strPrint = "error: " + write_string(error, false);
3584 int code = find_value(error.get_obj(), "code").get_int();
3585 nRet = abs(code);
3586 }
3587 else
3588 {
3589 // Result
3590 if (result.type() == null_type)
3591 strPrint = "";
3592 else if (result.type() == str_type)
3593 strPrint = result.get_str();
3594 else
3595 strPrint = write_string(result, true);
3596 }
3597 }
3598 catch (std::exception& e)
3599 {
3600 strPrint = string("error: ") + e.what();
3601 nRet = 87;
3602 }
3603 catch (...)
3604 {
3605 PrintException(NULL, "CommandLineRPC()");
3606 }
3607
3608 if (strPrint != "")
3609 {
3610 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
3611 }
3612 return nRet;
3613 }
3614
3615
3616
3617
3618 #ifdef TEST
3619 int main(int argc, char *argv[])
3620 {
3621 #ifdef _MSC_VER
3622 // Turn off microsoft heap dump noise
3623 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
3624 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
3625 #endif
3626 setbuf(stdin, NULL);
3627 setbuf(stdout, NULL);
3628 setbuf(stderr, NULL);
3629
3630 try
3631 {
3632 if (argc >= 2 && string(argv[1]) == "-server")
3633 {
3634 printf("server ready\n");
3635 ThreadRPCServer(NULL);
3636 }
3637 else
3638 {
3639 return CommandLineRPC(argc, argv);
3640 }
3641 }
3642 catch (std::exception& e) {
3643 PrintException(&e, "main()");
3644 } catch (...) {
3645 PrintException(NULL, "main()");
3646 }
3647 return 0;
3648 }
3649 #endif