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