2011-05-14 20:10:21 +02:00
|
|
|
// Copyright (c) 2010 Satoshi Nakamoto
|
2012-02-07 11:28:30 -05:00
|
|
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
2011-05-14 20:10:21 +02:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
2012-05-18 22:02:28 +08:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2012-04-15 22:10:54 +02:00
|
|
|
#include "main.h"
|
2011-06-18 18:46:01 +02:00
|
|
|
#include "net.h"
|
|
|
|
|
#include "init.h"
|
2012-08-21 10:38:57 -04:00
|
|
|
#include "util.h"
|
|
|
|
|
#include "sync.h"
|
2012-04-15 22:10:54 +02:00
|
|
|
#include "ui_interface.h"
|
2012-05-14 23:44:52 +02:00
|
|
|
#include "base58.h"
|
2012-04-21 01:37:34 +02:00
|
|
|
#include "bitcoinrpc.h"
|
2012-04-15 22:10:54 +02:00
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
#undef printf
|
|
|
|
|
#include <boost/asio.hpp>
|
2011-08-10 14:17:02 +02:00
|
|
|
#include <boost/asio/ip/v6_only.hpp>
|
2011-08-10 13:53:13 +02:00
|
|
|
#include <boost/bind.hpp>
|
2011-07-13 11:56:38 +02:00
|
|
|
#include <boost/filesystem.hpp>
|
2011-08-10 15:07:46 +02:00
|
|
|
#include <boost/foreach.hpp>
|
2011-05-14 20:10:21 +02:00
|
|
|
#include <boost/iostreams/concepts.hpp>
|
|
|
|
|
#include <boost/iostreams/stream.hpp>
|
2011-06-18 18:46:01 +02:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2011-07-11 21:49:45 +02:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2012-06-11 07:40:14 +02:00
|
|
|
#include <boost/asio/ssl.hpp>
|
2011-06-18 18:46:01 +02:00
|
|
|
#include <boost/filesystem/fstream.hpp>
|
2011-08-10 13:53:13 +02:00
|
|
|
#include <boost/shared_ptr.hpp>
|
2011-08-10 15:07:46 +02:00
|
|
|
#include <list>
|
2012-04-04 21:19:27 -04:00
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
#define printf OutputDebugStringF
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace boost;
|
|
|
|
|
using namespace boost::asio;
|
|
|
|
|
using namespace json_spirit;
|
|
|
|
|
|
|
|
|
|
void ThreadRPCServer2(void* parg);
|
|
|
|
|
|
2011-12-01 09:07:02 -05:00
|
|
|
static std::string strRPCUserColonPass;
|
|
|
|
|
|
2012-02-22 17:44:09 -05:00
|
|
|
const Object emptyobj;
|
|
|
|
|
|
2012-04-14 20:35:58 -04:00
|
|
|
void ThreadRPCServer3(void* parg);
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
Object JSONRPCError(int code, const string& message)
|
|
|
|
|
{
|
|
|
|
|
Object error;
|
|
|
|
|
error.push_back(Pair("code", code));
|
|
|
|
|
error.push_back(Pair("message", message));
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-22 18:36:42 -04:00
|
|
|
void RPCTypeCheck(const Array& params,
|
|
|
|
|
const list<Value_type>& typesExpected)
|
|
|
|
|
{
|
2012-07-05 13:25:52 -04:00
|
|
|
unsigned int i = 0;
|
2012-06-22 18:36:42 -04:00
|
|
|
BOOST_FOREACH(Value_type t, typesExpected)
|
|
|
|
|
{
|
|
|
|
|
if (params.size() <= i)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const Value& v = params[i];
|
|
|
|
|
if (v.type() != t)
|
|
|
|
|
{
|
|
|
|
|
string err = strprintf("Expected type %s, got %s",
|
|
|
|
|
Value_type_name[t], Value_type_name[v.type()]);
|
|
|
|
|
throw JSONRPCError(-3, err);
|
|
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RPCTypeCheck(const Object& o,
|
|
|
|
|
const map<string, Value_type>& typesExpected)
|
|
|
|
|
{
|
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
|
|
|
|
|
{
|
|
|
|
|
const Value& v = find_value(o, t.first);
|
|
|
|
|
if (v.type() == null_type)
|
|
|
|
|
throw JSONRPCError(-3, strprintf("Missing %s", t.first.c_str()));
|
|
|
|
|
if (v.type() != t.second)
|
|
|
|
|
{
|
|
|
|
|
string err = strprintf("Expected type %s for %s, got %s",
|
|
|
|
|
Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]);
|
|
|
|
|
throw JSONRPCError(-3, err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-23 16:26:38 -05:00
|
|
|
double GetDifficulty(const CBlockIndex* blockindex = NULL)
|
|
|
|
|
{
|
|
|
|
|
// Floating point number that is a multiple of the minimum difficulty,
|
|
|
|
|
// minimum difficulty = 1.0.
|
|
|
|
|
if (blockindex == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (pindexBest == NULL)
|
|
|
|
|
return 1.0;
|
|
|
|
|
else
|
|
|
|
|
blockindex = pindexBest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nShift = (blockindex->nBits >> 24) & 0xff;
|
|
|
|
|
|
|
|
|
|
double dDiff =
|
|
|
|
|
(double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff);
|
|
|
|
|
|
|
|
|
|
while (nShift < 29)
|
|
|
|
|
{
|
|
|
|
|
dDiff *= 256.0;
|
|
|
|
|
nShift++;
|
|
|
|
|
}
|
|
|
|
|
while (nShift > 29)
|
|
|
|
|
{
|
|
|
|
|
dDiff /= 256.0;
|
|
|
|
|
nShift--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dDiff;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2011-12-21 22:33:19 +01:00
|
|
|
int64 AmountFromValue(const Value& value)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
double dAmount = value.get_real();
|
|
|
|
|
if (dAmount <= 0.0 || dAmount > 21000000.0)
|
|
|
|
|
throw JSONRPCError(-3, "Invalid amount");
|
2011-12-21 22:33:19 +01:00
|
|
|
int64 nAmount = roundint64(dAmount * COIN);
|
2011-05-14 20:10:21 +02:00
|
|
|
if (!MoneyRange(nAmount))
|
|
|
|
|
throw JSONRPCError(-3, "Invalid amount");
|
|
|
|
|
return nAmount;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-21 22:33:19 +01:00
|
|
|
Value ValueFromAmount(int64 amount)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
return (double)amount / (double)COIN;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-22 12:12:28 -05:00
|
|
|
std::string
|
|
|
|
|
HexBits(unsigned int nBits)
|
|
|
|
|
{
|
|
|
|
|
union {
|
|
|
|
|
int32_t nBits;
|
|
|
|
|
char cBits[4];
|
|
|
|
|
} uBits;
|
|
|
|
|
uBits.nBits = htonl((int32_t)nBits);
|
|
|
|
|
return HexStr(BEGIN(uBits.cBits), END(uBits.cBits));
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-24 17:04:50 -04:00
|
|
|
Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
|
2011-12-23 16:26:38 -05:00
|
|
|
{
|
|
|
|
|
Object result;
|
|
|
|
|
result.push_back(Pair("hash", block.GetHash().GetHex()));
|
2012-02-22 13:26:25 -05:00
|
|
|
CMerkleTx txGen(block.vtx[0]);
|
|
|
|
|
txGen.SetMerkleBranch(&block);
|
|
|
|
|
result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain()));
|
2012-04-16 14:56:45 +02:00
|
|
|
result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
|
2012-02-22 12:12:28 -05:00
|
|
|
result.push_back(Pair("height", blockindex->nHeight));
|
2011-12-23 16:26:38 -05:00
|
|
|
result.push_back(Pair("version", block.nVersion));
|
|
|
|
|
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
|
2012-06-24 17:04:50 -04:00
|
|
|
Array txs;
|
|
|
|
|
BOOST_FOREACH(const CTransaction&tx, block.vtx)
|
|
|
|
|
txs.push_back(tx.GetHash().GetHex());
|
|
|
|
|
result.push_back(Pair("tx", txs));
|
2011-12-23 16:26:38 -05:00
|
|
|
result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime()));
|
|
|
|
|
result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce));
|
2012-02-22 12:12:28 -05:00
|
|
|
result.push_back(Pair("bits", HexBits(block.nBits)));
|
2011-12-23 16:26:38 -05:00
|
|
|
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
|
2012-02-22 17:44:09 -05:00
|
|
|
|
2011-12-23 16:26:38 -05:00
|
|
|
if (blockindex->pprev)
|
2012-02-22 12:12:28 -05:00
|
|
|
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
|
2011-12-23 16:26:38 -05:00
|
|
|
if (blockindex->pnext)
|
2012-02-22 12:12:28 -05:00
|
|
|
result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex()));
|
2011-12-23 16:26:38 -05:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
|
2012-02-22 17:44:09 -05:00
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
///
|
|
|
|
|
/// Note: This interface may still be subject to change.
|
|
|
|
|
///
|
|
|
|
|
|
2012-04-18 22:42:17 +02:00
|
|
|
string CRPCTable::help(string strCommand) const
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
string strRet;
|
|
|
|
|
set<rpcfn_type> setDone;
|
2012-04-18 22:42:17 +02:00
|
|
|
for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
2012-04-18 22:42:17 +02:00
|
|
|
const CRPCCommand *pcmd = mi->second;
|
2012-04-14 23:55:05 -04:00
|
|
|
string strMethod = mi->first;
|
2011-05-14 20:10:21 +02:00
|
|
|
// We already filter duplicates, but these deprecated screw up the sort order
|
2012-05-17 23:43:00 -04:00
|
|
|
if (strMethod.find("label") != string::npos)
|
2011-05-14 20:10:21 +02:00
|
|
|
continue;
|
|
|
|
|
if (strCommand != "" && strMethod != strCommand)
|
|
|
|
|
continue;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Array params;
|
2012-04-14 23:55:05 -04:00
|
|
|
rpcfn_type pfn = pcmd->actor;
|
2011-05-14 20:10:21 +02:00
|
|
|
if (setDone.insert(pfn).second)
|
|
|
|
|
(*pfn)(params, true);
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception& e)
|
|
|
|
|
{
|
|
|
|
|
// Help text is returned in an exception
|
|
|
|
|
string strHelp = string(e.what());
|
|
|
|
|
if (strCommand == "")
|
2012-04-15 16:47:24 -04:00
|
|
|
if (strHelp.find('\n') != string::npos)
|
2011-05-14 20:10:21 +02:00
|
|
|
strHelp = strHelp.substr(0, strHelp.find('\n'));
|
|
|
|
|
strRet += strHelp + "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (strRet == "")
|
|
|
|
|
strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
|
|
|
|
|
strRet = strRet.substr(0,strRet.size()-1);
|
|
|
|
|
return strRet;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 22:42:17 +02:00
|
|
|
Value help(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() > 1)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"help [command]\n"
|
|
|
|
|
"List commands, or get help for a command.");
|
|
|
|
|
|
|
|
|
|
string strCommand;
|
|
|
|
|
if (params.size() > 0)
|
|
|
|
|
strCommand = params[0].get_str();
|
|
|
|
|
|
|
|
|
|
return tableRPC.help(strCommand);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
Value stop(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() != 0)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"stop\n"
|
2012-05-13 16:09:14 +02:00
|
|
|
"Stop Bitcoin server.");
|
2011-05-14 20:10:21 +02:00
|
|
|
// Shutdown will take long enough that the response should get back
|
2012-06-11 07:40:14 +02:00
|
|
|
StartShutdown();
|
2012-05-13 16:09:14 +02:00
|
|
|
return "Bitcoin server stopping";
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value getblockcount(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() != 0)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"getblockcount\n"
|
|
|
|
|
"Returns the number of blocks in the longest block chain.");
|
|
|
|
|
|
|
|
|
|
return nBestHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value getdifficulty(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() != 0)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"getdifficulty\n"
|
|
|
|
|
"Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
|
|
|
|
|
|
|
|
|
|
return GetDifficulty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value getinfo(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() != 0)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"getinfo\n"
|
|
|
|
|
"Returns an object containing various state info.");
|
|
|
|
|
|
2012-05-24 19:02:21 +02:00
|
|
|
CService addrProxy;
|
|
|
|
|
GetProxy(NET_IPV4, addrProxy);
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
Object obj;
|
2011-12-16 16:26:14 -05:00
|
|
|
obj.push_back(Pair("version", (int)CLIENT_VERSION));
|
|
|
|
|
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
|
2012-03-22 03:56:31 +01:00
|
|
|
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
|
2011-06-26 19:23:24 +02:00
|
|
|
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
|
2011-05-14 20:10:21 +02:00
|
|
|
obj.push_back(Pair("blocks", (int)nBestHeight));
|
|
|
|
|
obj.push_back(Pair("connections", (int)vNodes.size()));
|
2012-05-24 19:02:21 +02:00
|
|
|
obj.push_back(Pair("proxy", (addrProxy.IsValid() ? addrProxy.ToStringIPPort() : string())));
|
2011-05-14 20:10:21 +02:00
|
|
|
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
|
|
|
|
|
obj.push_back(Pair("testnet", fTestNet));
|
2011-06-26 19:23:24 +02:00
|
|
|
obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
|
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 15:47:35 +02:00
|
|
|
obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
|
2011-05-14 20:10:21 +02:00
|
|
|
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
|
2011-06-29 00:47:41 +02:00
|
|
|
if (pwalletMain->IsCrypted())
|
2012-02-11 18:01:24 +01:00
|
|
|
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
|
2011-05-14 20:10:21 +02:00
|
|
|
obj.push_back(Pair("errors", GetWarnings("statusbar")));
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value settxfee(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() < 1 || params.size() > 1)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"settxfee <amount>\n"
|
|
|
|
|
"<amount> is a real and is rounded to the nearest 0.00000001");
|
|
|
|
|
|
|
|
|
|
// Amount
|
2011-12-21 22:33:19 +01:00
|
|
|
int64 nAmount = 0;
|
2011-05-14 20:10:21 +02:00
|
|
|
if (params[0].get_real() != 0.0)
|
|
|
|
|
nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
|
|
|
|
|
|
|
|
|
|
nTransactionFee = nAmount;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-22 11:43:34 -04:00
|
|
|
Value getrawmempool(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() != 0)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"getrawmempool\n"
|
|
|
|
|
"Returns all transaction ids in memory pool.");
|
|
|
|
|
|
|
|
|
|
vector<uint256> vtxid;
|
|
|
|
|
mempool.queryHashes(vtxid);
|
|
|
|
|
|
|
|
|
|
Array a;
|
|
|
|
|
BOOST_FOREACH(const uint256& hash, vtxid)
|
|
|
|
|
a.push_back(hash.ToString());
|
|
|
|
|
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-23 16:26:38 -05:00
|
|
|
Value getblockhash(const Array& params, bool fHelp)
|
|
|
|
|
{
|
|
|
|
|
if (fHelp || params.size() != 1)
|
|
|
|
|
throw runtime_error(
|
|
|
|
|
"getblockhash <index>\n"
|
|
|
|
|
"Returns hash of block in best-block-chain at <index>.");
|
|
|
|
|
|
|
|
|
|
int nHeight = params[0].get_int();
|
|
|
|
|
if (nHeight < 0 || nHeight > nBestHeight)
|
|
|
|
|
throw runtime_error("Block number out of range.");
|
|
|
|
|
|
2012-07-19 20:06:20 +00:00
|
|
|
CBlockIndex* pblockindex = FindBlockByHeight(nHeight);
|
2011-12-23 16:26:38 -05:00
|
|
|
return pblockindex->phashBlock->GetHex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Value getblock(const Array& params, bool fHelp)
|
|
|
|
|
{
|
2012-06-24 17:04:50 -04:00
|
|
|
if (fHelp || params.size() != 1)
|
2011-12-23 16:26:38 -05:00
|
|
|
throw runtime_error(
|
2012-06-24 17:04:50 -04:00
|
|
|
"getblock <hash>\n"
|
2011-12-23 16:26:38 -05:00
|
|
|
"Returns details of a block with given block-hash.");
|
|
|
|
|
|
|
|
|
|
std::string strHash = params[0].get_str();
|
|
|
|
|
uint256 hash(strHash);
|
|
|
|
|
|
|
|
|
|
if (mapBlockIndex.count(hash) == 0)
|
|
|
|
|
throw JSONRPCError(-5, "Block not found");
|
|
|
|
|
|
|
|
|
|
CBlock block;
|
|
|
|
|
CBlockIndex* pblockindex = mapBlockIndex[hash];
|
|
|
|
|
block.ReadFromDisk(pblockindex, true);
|
|
|
|
|
|
2012-06-24 17:04:50 -04:00
|
|
|
return blockToJSON(block, pblockindex);
|
2011-12-23 16:26:38 -05:00
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Call Table
|
|
|
|
|
//
|
|
|
|
|
|
2012-04-14 23:55:05 -04:00
|
|
|
|
2012-04-21 01:37:34 +02:00
|
|
|
static const CRPCCommand vRPCCommands[] =
|
2012-04-14 23:55:05 -04:00
|
|
|
{ // name function safe mode?
|
|
|
|
|
// ------------------------ ----------------------- ----------
|
|
|
|
|
{ "help", &help, true },
|
|
|
|
|
{ "stop", &stop, true },
|
|
|
|
|
{ "getblockcount", &getblockcount, true },
|
|
|
|
|
{ "getconnectioncount", &getconnectioncount, true },
|
2012-06-29 17:24:53 -04:00
|
|
|
{ "getpeerinfo", &getpeerinfo, true },
|
2012-04-14 23:55:05 -04:00
|
|
|
{ "getdifficulty", &getdifficulty, true },
|
|
|
|
|
{ "getgenerate", &getgenerate, true },
|
|
|
|
|
{ "setgenerate", &setgenerate, true },
|
|
|
|
|
{ "gethashespersec", &gethashespersec, true },
|
|
|
|
|
{ "getinfo", &getinfo, true },
|
|
|
|
|
{ "getmininginfo", &getmininginfo, true },
|
|
|
|
|
{ "getnewaddress", &getnewaddress, true },
|
|
|
|
|
{ "getaccountaddress", &getaccountaddress, true },
|
|
|
|
|
{ "setaccount", &setaccount, true },
|
|
|
|
|
{ "getaccount", &getaccount, false },
|
|
|
|
|
{ "getaddressesbyaccount", &getaddressesbyaccount, true },
|
|
|
|
|
{ "sendtoaddress", &sendtoaddress, false },
|
|
|
|
|
{ "getreceivedbyaddress", &getreceivedbyaddress, false },
|
|
|
|
|
{ "getreceivedbyaccount", &getreceivedbyaccount, false },
|
|
|
|
|
{ "listreceivedbyaddress", &listreceivedbyaddress, false },
|
|
|
|
|
{ "listreceivedbyaccount", &listreceivedbyaccount, false },
|
|
|
|
|
{ "backupwallet", &backupwallet, true },
|
|
|
|
|
{ "keypoolrefill", &keypoolrefill, true },
|
|
|
|
|
{ "walletpassphrase", &walletpassphrase, true },
|
|
|
|
|
{ "walletpassphrasechange", &walletpassphrasechange, false },
|
|
|
|
|
{ "walletlock", &walletlock, true },
|
|
|
|
|
{ "encryptwallet", &encryptwallet, false },
|
|
|
|
|
{ "validateaddress", &validateaddress, true },
|
|
|
|
|
{ "getbalance", &getbalance, false },
|
|
|
|
|
{ "move", &movecmd, false },
|
|
|
|
|
{ "sendfrom", &sendfrom, false },
|
|
|
|
|
{ "sendmany", &sendmany, false },
|
|
|
|
|
{ "addmultisigaddress", &addmultisigaddress, false },
|
2012-06-22 11:43:34 -04:00
|
|
|
{ "getrawmempool", &getrawmempool, true },
|
2012-04-14 23:55:05 -04:00
|
|
|
{ "getblock", &getblock, false },
|
|
|
|
|
{ "getblockhash", &getblockhash, false },
|
|
|
|
|
{ "gettransaction", &gettransaction, false },
|
|
|
|
|
{ "listtransactions", &listtransactions, false },
|
|
|
|
|
{ "signmessage", &signmessage, false },
|
|
|
|
|
{ "verifymessage", &verifymessage, false },
|
|
|
|
|
{ "getwork", &getwork, true },
|
|
|
|
|
{ "listaccounts", &listaccounts, false },
|
|
|
|
|
{ "settxfee", &settxfee, false },
|
2012-08-03 01:12:55 +00:00
|
|
|
{ "getblocktemplate", &getblocktemplate, true },
|
2012-08-21 02:02:06 -04:00
|
|
|
{ "submitblock", &submitblock, false },
|
2012-04-14 23:55:05 -04:00
|
|
|
{ "listsinceblock", &listsinceblock, false },
|
|
|
|
|
{ "dumpprivkey", &dumpprivkey, false },
|
|
|
|
|
{ "importprivkey", &importprivkey, false },
|
2012-05-31 16:01:16 -04:00
|
|
|
{ "listunspent", &listunspent, false },
|
|
|
|
|
{ "getrawtransaction", &getrawtransaction, false },
|
|
|
|
|
{ "createrawtransaction", &createrawtransaction, false },
|
|
|
|
|
{ "decoderawtransaction", &decoderawtransaction, false },
|
|
|
|
|
{ "signrawtransaction", &signrawtransaction, false },
|
|
|
|
|
{ "sendrawtransaction", &sendrawtransaction, false },
|
2011-05-14 20:10:21 +02:00
|
|
|
};
|
|
|
|
|
|
2012-04-18 22:42:17 +02:00
|
|
|
CRPCTable::CRPCTable()
|
2012-04-14 23:55:05 -04:00
|
|
|
{
|
|
|
|
|
unsigned int vcidx;
|
|
|
|
|
for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
|
|
|
|
|
{
|
2012-04-21 01:37:34 +02:00
|
|
|
const CRPCCommand *pcmd;
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2012-04-14 23:55:05 -04:00
|
|
|
pcmd = &vRPCCommands[vcidx];
|
|
|
|
|
mapCommands[pcmd->name] = pcmd;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2012-04-18 22:42:17 +02:00
|
|
|
const CRPCCommand *CRPCTable::operator[](string name) const
|
|
|
|
|
{
|
|
|
|
|
map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
|
|
|
|
|
if (it == mapCommands.end())
|
|
|
|
|
return NULL;
|
|
|
|
|
return (*it).second;
|
|
|
|
|
}
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// HTTP protocol
|
|
|
|
|
//
|
|
|
|
|
// This ain't Apache. We're just using HTTP header for the length field
|
|
|
|
|
// and to be compatible with other JSON-RPC implementations.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
|
|
|
|
|
{
|
|
|
|
|
ostringstream s;
|
|
|
|
|
s << "POST / HTTP/1.1\r\n"
|
|
|
|
|
<< "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
|
|
|
|
|
<< "Host: 127.0.0.1\r\n"
|
|
|
|
|
<< "Content-Type: application/json\r\n"
|
|
|
|
|
<< "Content-Length: " << strMsg.size() << "\r\n"
|
2011-10-04 00:42:36 -04:00
|
|
|
<< "Connection: close\r\n"
|
2011-05-14 20:10:21 +02:00
|
|
|
<< "Accept: application/json\r\n";
|
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
|
|
|
|
|
s << item.first << ": " << item.second << "\r\n";
|
|
|
|
|
s << "\r\n" << strMsg;
|
|
|
|
|
|
|
|
|
|
return s.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string rfc1123Time()
|
|
|
|
|
{
|
|
|
|
|
char buffer[64];
|
|
|
|
|
time_t now;
|
|
|
|
|
time(&now);
|
|
|
|
|
struct tm* now_gmt = gmtime(&now);
|
|
|
|
|
string locale(setlocale(LC_TIME, NULL));
|
2012-07-26 00:48:39 +00:00
|
|
|
setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
|
2011-05-14 20:10:21 +02:00
|
|
|
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
|
|
|
|
|
setlocale(LC_TIME, locale.c_str());
|
|
|
|
|
return string(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 01:10:02 -04:00
|
|
|
static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
if (nStatus == 401)
|
|
|
|
|
return strprintf("HTTP/1.0 401 Authorization Required\r\n"
|
|
|
|
|
"Date: %s\r\n"
|
|
|
|
|
"Server: bitcoin-json-rpc/%s\r\n"
|
|
|
|
|
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"Content-Length: 296\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
|
|
|
|
|
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
|
|
|
|
|
"<HTML>\r\n"
|
|
|
|
|
"<HEAD>\r\n"
|
|
|
|
|
"<TITLE>Error</TITLE>\r\n"
|
|
|
|
|
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
|
|
|
|
|
"</HEAD>\r\n"
|
|
|
|
|
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
|
|
|
|
|
"</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
|
2011-08-06 05:15:00 -08:00
|
|
|
const char *cStatus;
|
|
|
|
|
if (nStatus == 200) cStatus = "OK";
|
|
|
|
|
else if (nStatus == 400) cStatus = "Bad Request";
|
|
|
|
|
else if (nStatus == 403) cStatus = "Forbidden";
|
|
|
|
|
else if (nStatus == 404) cStatus = "Not Found";
|
|
|
|
|
else if (nStatus == 500) cStatus = "Internal Server Error";
|
|
|
|
|
else cStatus = "";
|
2011-05-14 20:10:21 +02:00
|
|
|
return strprintf(
|
|
|
|
|
"HTTP/1.1 %d %s\r\n"
|
|
|
|
|
"Date: %s\r\n"
|
2012-04-24 01:10:02 -04:00
|
|
|
"Connection: %s\r\n"
|
2011-05-14 20:10:21 +02:00
|
|
|
"Content-Length: %d\r\n"
|
|
|
|
|
"Content-Type: application/json\r\n"
|
|
|
|
|
"Server: bitcoin-json-rpc/%s\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"%s",
|
|
|
|
|
nStatus,
|
2011-08-06 05:15:00 -08:00
|
|
|
cStatus,
|
2011-05-14 20:10:21 +02:00
|
|
|
rfc1123Time().c_str(),
|
2012-04-24 01:10:02 -04:00
|
|
|
keepalive ? "keep-alive" : "close",
|
2011-05-14 20:10:21 +02:00
|
|
|
strMsg.size(),
|
|
|
|
|
FormatFullVersion().c_str(),
|
|
|
|
|
strMsg.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 01:10:02 -04:00
|
|
|
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
string str;
|
|
|
|
|
getline(stream, str);
|
|
|
|
|
vector<string> vWords;
|
|
|
|
|
boost::split(vWords, str, boost::is_any_of(" "));
|
|
|
|
|
if (vWords.size() < 2)
|
|
|
|
|
return 500;
|
2012-04-24 01:10:02 -04:00
|
|
|
proto = 0;
|
|
|
|
|
const char *ver = strstr(str.c_str(), "HTTP/1.");
|
|
|
|
|
if (ver != NULL)
|
|
|
|
|
proto = atoi(ver+7);
|
2011-05-14 20:10:21 +02:00
|
|
|
return atoi(vWords[1].c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
|
|
|
|
|
{
|
|
|
|
|
int nLen = 0;
|
|
|
|
|
loop
|
|
|
|
|
{
|
|
|
|
|
string str;
|
|
|
|
|
std::getline(stream, str);
|
|
|
|
|
if (str.empty() || str == "\r")
|
|
|
|
|
break;
|
|
|
|
|
string::size_type nColon = str.find(":");
|
|
|
|
|
if (nColon != string::npos)
|
|
|
|
|
{
|
|
|
|
|
string strHeader = str.substr(0, nColon);
|
|
|
|
|
boost::trim(strHeader);
|
|
|
|
|
boost::to_lower(strHeader);
|
|
|
|
|
string strValue = str.substr(nColon+1);
|
|
|
|
|
boost::trim(strValue);
|
|
|
|
|
mapHeadersRet[strHeader] = strValue;
|
|
|
|
|
if (strHeader == "content-length")
|
|
|
|
|
nLen = atoi(strValue.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
|
|
|
|
|
{
|
|
|
|
|
mapHeadersRet.clear();
|
|
|
|
|
strMessageRet = "";
|
|
|
|
|
|
|
|
|
|
// Read status
|
2012-05-13 01:34:38 +02:00
|
|
|
int nProto = 0;
|
2012-04-24 01:10:02 -04:00
|
|
|
int nStatus = ReadHTTPStatus(stream, nProto);
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
// Read header
|
|
|
|
|
int nLen = ReadHTTPHeader(stream, mapHeadersRet);
|
2012-04-22 13:51:16 -04:00
|
|
|
if (nLen < 0 || nLen > (int)MAX_SIZE)
|
2011-05-14 20:10:21 +02:00
|
|
|
return 500;
|
|
|
|
|
|
|
|
|
|
// Read message
|
|
|
|
|
if (nLen > 0)
|
|
|
|
|
{
|
|
|
|
|
vector<char> vch(nLen);
|
|
|
|
|
stream.read(&vch[0], nLen);
|
|
|
|
|
strMessageRet = string(vch.begin(), vch.end());
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 01:10:02 -04:00
|
|
|
string sConHdr = mapHeadersRet["connection"];
|
|
|
|
|
|
|
|
|
|
if ((sConHdr != "close") && (sConHdr != "keep-alive"))
|
|
|
|
|
{
|
|
|
|
|
if (nProto >= 1)
|
|
|
|
|
mapHeadersRet["connection"] = "keep-alive";
|
|
|
|
|
else
|
|
|
|
|
mapHeadersRet["connection"] = "close";
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
return nStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool HTTPAuthorized(map<string, string>& mapHeaders)
|
|
|
|
|
{
|
|
|
|
|
string strAuth = mapHeaders["authorization"];
|
|
|
|
|
if (strAuth.substr(0,6) != "Basic ")
|
|
|
|
|
return false;
|
|
|
|
|
string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
|
|
|
|
|
string strUserPass = DecodeBase64(strUserPass64);
|
2011-12-01 09:07:02 -05:00
|
|
|
return strUserPass == strRPCUserColonPass;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
|
|
|
|
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
|
|
|
|
// unspecified (HTTP errors and contents of 'error').
|
|
|
|
|
//
|
|
|
|
|
// 1.0 spec: http://json-rpc.org/wiki/specification
|
|
|
|
|
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
|
|
|
|
// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
|
|
|
|
|
{
|
|
|
|
|
Object request;
|
|
|
|
|
request.push_back(Pair("method", strMethod));
|
|
|
|
|
request.push_back(Pair("params", params));
|
|
|
|
|
request.push_back(Pair("id", id));
|
|
|
|
|
return write_string(Value(request), false) + "\n";
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-27 13:47:02 -04:00
|
|
|
Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
Object reply;
|
|
|
|
|
if (error.type() != null_type)
|
|
|
|
|
reply.push_back(Pair("result", Value::null));
|
|
|
|
|
else
|
|
|
|
|
reply.push_back(Pair("result", result));
|
|
|
|
|
reply.push_back(Pair("error", error));
|
|
|
|
|
reply.push_back(Pair("id", id));
|
2012-06-27 13:47:02 -04:00
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string JSONRPCReply(const Value& result, const Value& error, const Value& id)
|
|
|
|
|
{
|
|
|
|
|
Object reply = JSONRPCReplyObj(result, error, id);
|
2011-05-14 20:10:21 +02:00
|
|
|
return write_string(Value(reply), false) + "\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
|
|
|
|
|
{
|
|
|
|
|
// Send error reply from json-rpc error object
|
|
|
|
|
int nStatus = 500;
|
|
|
|
|
int code = find_value(objError, "code").get_int();
|
|
|
|
|
if (code == -32600) nStatus = 400;
|
|
|
|
|
else if (code == -32601) nStatus = 404;
|
|
|
|
|
string strReply = JSONRPCReply(Value::null, objError, id);
|
2012-04-24 01:10:02 -04:00
|
|
|
stream << HTTPReply(nStatus, strReply, false) << std::flush;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
|
2011-08-10 14:21:43 +02:00
|
|
|
bool ClientAllowed(const boost::asio::ip::address& address)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
2011-08-10 14:21:43 +02:00
|
|
|
// Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
|
|
|
|
|
if (address.is_v6()
|
|
|
|
|
&& (address.to_v6().is_v4_compatible()
|
|
|
|
|
|| address.to_v6().is_v4_mapped()))
|
|
|
|
|
return ClientAllowed(address.to_v6().to_v4());
|
|
|
|
|
|
|
|
|
|
if (address == asio::ip::address_v4::loopback()
|
2012-05-20 17:46:44 +02:00
|
|
|
|| address == asio::ip::address_v6::loopback()
|
|
|
|
|
|| (address.is_v4()
|
2012-07-26 00:48:39 +00:00
|
|
|
// Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
|
2012-05-20 17:46:44 +02:00
|
|
|
&& (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000))
|
2011-05-14 20:10:21 +02:00
|
|
|
return true;
|
2011-08-10 14:21:43 +02:00
|
|
|
|
|
|
|
|
const string strAddress = address.to_string();
|
2011-05-14 20:10:21 +02:00
|
|
|
const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
|
|
|
|
|
BOOST_FOREACH(string strAllow, vAllow)
|
|
|
|
|
if (WildcardMatch(strAddress, strAllow))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// IOStream device that speaks SSL but can also speak non-SSL
|
|
|
|
|
//
|
2011-08-10 15:07:46 +02:00
|
|
|
template <typename Protocol>
|
2011-05-14 20:10:21 +02:00
|
|
|
class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
|
|
|
|
|
public:
|
2011-08-10 15:07:46 +02:00
|
|
|
SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
|
|
|
|
fUseSSL = fUseSSLIn;
|
|
|
|
|
fNeedHandshake = fUseSSLIn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handshake(ssl::stream_base::handshake_type role)
|
|
|
|
|
{
|
|
|
|
|
if (!fNeedHandshake) return;
|
|
|
|
|
fNeedHandshake = false;
|
|
|
|
|
stream.handshake(role);
|
|
|
|
|
}
|
|
|
|
|
std::streamsize read(char* s, std::streamsize n)
|
|
|
|
|
{
|
|
|
|
|
handshake(ssl::stream_base::server); // HTTPS servers read first
|
|
|
|
|
if (fUseSSL) return stream.read_some(asio::buffer(s, n));
|
|
|
|
|
return stream.next_layer().read_some(asio::buffer(s, n));
|
|
|
|
|
}
|
|
|
|
|
std::streamsize write(const char* s, std::streamsize n)
|
|
|
|
|
{
|
|
|
|
|
handshake(ssl::stream_base::client); // HTTPS clients write first
|
|
|
|
|
if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
|
|
|
|
|
return asio::write(stream.next_layer(), asio::buffer(s, n));
|
|
|
|
|
}
|
|
|
|
|
bool connect(const std::string& server, const std::string& port)
|
|
|
|
|
{
|
|
|
|
|
ip::tcp::resolver resolver(stream.get_io_service());
|
|
|
|
|
ip::tcp::resolver::query query(server.c_str(), port.c_str());
|
|
|
|
|
ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
|
|
|
|
|
ip::tcp::resolver::iterator end;
|
|
|
|
|
boost::system::error_code error = asio::error::host_not_found;
|
|
|
|
|
while (error && endpoint_iterator != end)
|
|
|
|
|
{
|
|
|
|
|
stream.lowest_layer().close();
|
|
|
|
|
stream.lowest_layer().connect(*endpoint_iterator++, error);
|
|
|
|
|
}
|
|
|
|
|
if (error)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool fNeedHandshake;
|
|
|
|
|
bool fUseSSL;
|
2011-08-10 15:07:46 +02:00
|
|
|
asio::ssl::stream<typename Protocol::socket>& stream;
|
2011-05-14 20:10:21 +02:00
|
|
|
};
|
|
|
|
|
|
2012-04-14 20:35:58 -04:00
|
|
|
class AcceptedConnection
|
|
|
|
|
{
|
2011-08-10 15:07:46 +02:00
|
|
|
public:
|
|
|
|
|
virtual ~AcceptedConnection() {}
|
|
|
|
|
|
|
|
|
|
virtual std::iostream& stream() = 0;
|
|
|
|
|
virtual std::string peer_address_to_string() const = 0;
|
|
|
|
|
virtual void close() = 0;
|
|
|
|
|
};
|
2012-04-14 20:35:58 -04:00
|
|
|
|
2011-08-10 15:07:46 +02:00
|
|
|
template <typename Protocol>
|
|
|
|
|
class AcceptedConnectionImpl : public AcceptedConnection
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AcceptedConnectionImpl(
|
|
|
|
|
asio::io_service& io_service,
|
|
|
|
|
ssl::context &context,
|
|
|
|
|
bool fUseSSL) :
|
|
|
|
|
sslStream(io_service, context),
|
|
|
|
|
_d(sslStream, fUseSSL),
|
|
|
|
|
_stream(_d)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual std::iostream& stream()
|
|
|
|
|
{
|
|
|
|
|
return _stream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual std::string peer_address_to_string() const
|
|
|
|
|
{
|
|
|
|
|
return peer.address().to_string();
|
|
|
|
|
}
|
2012-04-14 20:35:58 -04:00
|
|
|
|
2011-08-10 15:07:46 +02:00
|
|
|
virtual void close()
|
|
|
|
|
{
|
|
|
|
|
_stream.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typename Protocol::endpoint peer;
|
|
|
|
|
asio::ssl::stream<typename Protocol::socket> sslStream;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
SSLIOStreamDevice<Protocol> _d;
|
|
|
|
|
iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
|
2012-04-14 20:35:58 -04:00
|
|
|
};
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
void ThreadRPCServer(void* parg)
|
|
|
|
|
{
|
|
|
|
|
IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
|
2012-06-24 17:03:57 +02:00
|
|
|
|
|
|
|
|
// Make this thread recognisable as the RPC listener
|
|
|
|
|
RenameThread("bitcoin-rpclist");
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
try
|
|
|
|
|
{
|
2012-04-14 20:35:58 -04:00
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]++;
|
2011-05-14 20:10:21 +02:00
|
|
|
ThreadRPCServer2(parg);
|
2012-04-14 20:35:58 -04:00
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
catch (std::exception& e) {
|
2012-04-14 20:35:58 -04:00
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
2011-05-14 20:10:21 +02:00
|
|
|
PrintException(&e, "ThreadRPCServer()");
|
|
|
|
|
} catch (...) {
|
2012-04-14 20:35:58 -04:00
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
2011-05-14 20:10:21 +02:00
|
|
|
PrintException(NULL, "ThreadRPCServer()");
|
|
|
|
|
}
|
2012-05-17 18:52:38 +01:00
|
|
|
printf("ThreadRPCServer exited\n");
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
|
2011-08-10 13:53:13 +02:00
|
|
|
// Forward declaration required for RPCListen
|
2011-08-10 15:07:46 +02:00
|
|
|
template <typename Protocol, typename SocketAcceptorService>
|
|
|
|
|
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
|
2011-08-10 13:53:13 +02:00
|
|
|
ssl::context& context,
|
|
|
|
|
bool fUseSSL,
|
|
|
|
|
AcceptedConnection* conn,
|
|
|
|
|
const boost::system::error_code& error);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets up I/O resources to accept and handle a new connection.
|
|
|
|
|
*/
|
2011-08-10 15:07:46 +02:00
|
|
|
template <typename Protocol, typename SocketAcceptorService>
|
|
|
|
|
static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
|
2011-08-10 13:53:13 +02:00
|
|
|
ssl::context& context,
|
|
|
|
|
const bool fUseSSL)
|
|
|
|
|
{
|
|
|
|
|
// Accept connection
|
2011-08-10 15:07:46 +02:00
|
|
|
AcceptedConnectionImpl<Protocol>* conn = new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL);
|
2011-08-10 13:53:13 +02:00
|
|
|
|
|
|
|
|
acceptor->async_accept(
|
|
|
|
|
conn->sslStream.lowest_layer(),
|
|
|
|
|
conn->peer,
|
2011-08-10 15:07:46 +02:00
|
|
|
boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
|
2011-08-10 13:53:13 +02:00
|
|
|
acceptor,
|
|
|
|
|
boost::ref(context),
|
|
|
|
|
fUseSSL,
|
|
|
|
|
conn,
|
|
|
|
|
boost::asio::placeholders::error));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Accept and handle incoming connection.
|
|
|
|
|
*/
|
2011-08-10 15:07:46 +02:00
|
|
|
template <typename Protocol, typename SocketAcceptorService>
|
|
|
|
|
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
|
2011-08-10 13:53:13 +02:00
|
|
|
ssl::context& context,
|
|
|
|
|
const bool fUseSSL,
|
|
|
|
|
AcceptedConnection* conn,
|
|
|
|
|
const boost::system::error_code& error)
|
|
|
|
|
{
|
|
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]++;
|
|
|
|
|
|
2012-07-26 00:48:39 +00:00
|
|
|
// Immediately start accepting new connections, except when we're cancelled or our socket is closed.
|
2012-06-28 19:31:22 +02:00
|
|
|
if (error != asio::error::operation_aborted
|
2012-06-24 13:20:17 +02:00
|
|
|
&& acceptor->is_open())
|
|
|
|
|
RPCListen(acceptor, context, fUseSSL);
|
2011-08-10 13:53:13 +02:00
|
|
|
|
2011-08-10 15:07:46 +02:00
|
|
|
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
|
|
|
|
|
|
2011-08-10 13:53:13 +02:00
|
|
|
// TODO: Actually handle errors
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
|
delete conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restrict callers by IP. It is important to
|
|
|
|
|
// do this before starting client thread, to filter out
|
|
|
|
|
// certain DoS and misbehaving clients.
|
2011-08-10 15:07:46 +02:00
|
|
|
else if (tcp_conn
|
|
|
|
|
&& !ClientAllowed(tcp_conn->peer.address()))
|
2011-08-10 13:53:13 +02:00
|
|
|
{
|
|
|
|
|
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
|
|
|
|
|
if (!fUseSSL)
|
2011-08-10 15:07:46 +02:00
|
|
|
conn->stream() << HTTPReply(403, "", false) << std::flush;
|
2011-08-10 13:53:13 +02:00
|
|
|
delete conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// start HTTP client thread
|
|
|
|
|
else if (!CreateThread(ThreadRPCServer3, conn)) {
|
|
|
|
|
printf("Failed to create RPC server client thread\n");
|
|
|
|
|
delete conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
void ThreadRPCServer2(void* parg)
|
|
|
|
|
{
|
|
|
|
|
printf("ThreadRPCServer started\n");
|
|
|
|
|
|
2011-12-01 09:07:02 -05:00
|
|
|
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
|
2012-02-05 02:30:43 -05:00
|
|
|
if (mapArgs["-rpcpassword"] == "")
|
2011-05-14 20:10:21 +02:00
|
|
|
{
|
2012-02-05 02:30:43 -05:00
|
|
|
unsigned char rand_pwd[32];
|
|
|
|
|
RAND_bytes(rand_pwd, 32);
|
2011-05-14 20:10:21 +02:00
|
|
|
string strWhatAmI = "To use bitcoind";
|
|
|
|
|
if (mapArgs.count("-server"))
|
|
|
|
|
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
|
|
|
|
|
else if (mapArgs.count("-daemon"))
|
|
|
|
|
strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
|
2012-05-06 19:40:58 +02:00
|
|
|
uiInterface.ThreadSafeMessageBox(strprintf(
|
2012-03-31 15:08:25 +02:00
|
|
|
_("%s, you must set a rpcpassword in the configuration file:\n %s\n"
|
|
|
|
|
"It is recommended you use the following random password:\n"
|
|
|
|
|
"rpcuser=bitcoinrpc\n"
|
|
|
|
|
"rpcpassword=%s\n"
|
|
|
|
|
"(you do not need to remember this password)\n"
|
|
|
|
|
"If the file does not exist, create it with owner-readable-only file permissions.\n"),
|
2011-05-14 20:10:21 +02:00
|
|
|
strWhatAmI.c_str(),
|
2012-04-09 23:50:56 +02:00
|
|
|
GetConfigFile().string().c_str(),
|
2012-03-31 15:08:25 +02:00
|
|
|
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
|
2012-05-19 09:35:26 +02:00
|
|
|
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
|
2012-06-11 07:40:14 +02:00
|
|
|
StartShutdown();
|
2011-05-14 20:10:21 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-10 13:53:13 +02:00
|
|
|
const bool fUseSSL = GetBoolArg("-rpcssl");
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
asio::io_service io_service;
|
2012-05-20 20:27:53 +02:00
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
ssl::context context(io_service, ssl::context::sslv23);
|
|
|
|
|
if (fUseSSL)
|
|
|
|
|
{
|
|
|
|
|
context.set_options(ssl::context::no_sslv2);
|
2012-03-31 15:05:55 +02:00
|
|
|
|
|
|
|
|
filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
|
|
|
|
|
if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
|
2012-04-09 23:50:56 +02:00
|
|
|
if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string());
|
2012-03-31 15:05:55 +02:00
|
|
|
else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
|
|
|
|
|
|
|
|
|
|
filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
|
|
|
|
|
if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
|
2012-04-09 23:50:56 +02:00
|
|
|
if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem);
|
2012-03-31 15:05:55 +02:00
|
|
|
else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
|
|
|
|
|
|
|
|
|
|
string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
|
|
|
|
|
SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str());
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
|
2011-08-10 14:17:02 +02:00
|
|
|
// Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
|
|
|
|
|
const bool loopback = !mapArgs.count("-rpcallowip");
|
|
|
|
|
asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
|
|
|
|
|
ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
|
|
|
|
|
|
2012-06-28 18:32:32 +02:00
|
|
|
boost::signals2::signal<void ()> StopRequests;
|
|
|
|
|
|
2011-08-10 14:17:02 +02:00
|
|
|
try
|
|
|
|
|
{
|
2012-06-24 13:20:17 +02:00
|
|
|
boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(io_service));
|
|
|
|
|
acceptor->open(endpoint.protocol());
|
|
|
|
|
acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
2011-08-10 14:17:02 +02:00
|
|
|
|
|
|
|
|
// Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
|
|
|
|
|
boost::system::error_code v6_only_error;
|
2012-06-24 13:20:17 +02:00
|
|
|
acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error);
|
2011-08-10 14:17:02 +02:00
|
|
|
|
2012-06-24 13:20:17 +02:00
|
|
|
acceptor->bind(endpoint);
|
|
|
|
|
acceptor->listen(socket_base::max_connections);
|
2011-08-10 14:17:02 +02:00
|
|
|
|
2012-06-24 13:20:17 +02:00
|
|
|
RPCListen(acceptor, context, fUseSSL);
|
|
|
|
|
// Cancel outstanding listen-requests for this acceptor when shutting down
|
2012-06-28 18:32:32 +02:00
|
|
|
StopRequests.connect(signals2::slot<void ()>(
|
2012-06-24 13:20:17 +02:00
|
|
|
static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
|
|
|
|
|
.track(acceptor));
|
2011-08-10 14:17:02 +02:00
|
|
|
|
|
|
|
|
// If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
|
|
|
|
|
if (loopback || v6_only_error)
|
|
|
|
|
{
|
|
|
|
|
bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
|
|
|
|
|
endpoint.address(bindAddress);
|
|
|
|
|
|
2012-06-24 13:20:17 +02:00
|
|
|
acceptor.reset(new ip::tcp::acceptor(io_service));
|
|
|
|
|
acceptor->open(endpoint.protocol());
|
|
|
|
|
acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
|
|
|
|
acceptor->bind(endpoint);
|
|
|
|
|
acceptor->listen(socket_base::max_connections);
|
|
|
|
|
|
|
|
|
|
RPCListen(acceptor, context, fUseSSL);
|
|
|
|
|
// Cancel outstanding listen-requests for this acceptor when shutting down
|
2012-06-28 18:32:32 +02:00
|
|
|
StopRequests.connect(signals2::slot<void ()>(
|
2012-06-24 13:20:17 +02:00
|
|
|
static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
|
|
|
|
|
.track(acceptor));
|
2011-08-10 14:17:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch(boost::system::system_error &e)
|
|
|
|
|
{
|
2012-07-26 00:48:39 +00:00
|
|
|
uiInterface.ThreadSafeMessageBox(strprintf(_("An error occurred while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
|
2011-08-10 14:17:02 +02:00
|
|
|
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
|
2012-06-17 14:30:37 +02:00
|
|
|
StartShutdown();
|
2011-08-10 14:17:02 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2011-08-10 13:53:13 +02:00
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
2012-06-28 15:52:45 +02:00
|
|
|
while (!fShutdown)
|
|
|
|
|
io_service.run_one();
|
2011-08-10 13:53:13 +02:00
|
|
|
vnThreadsRunning[THREAD_RPCLISTENER]++;
|
2012-06-28 18:32:32 +02:00
|
|
|
StopRequests();
|
2012-04-14 20:35:58 -04:00
|
|
|
}
|
|
|
|
|
|
2012-06-27 13:47:02 -04:00
|
|
|
class JSONRequest
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Value id;
|
|
|
|
|
string strMethod;
|
|
|
|
|
Array params;
|
|
|
|
|
|
|
|
|
|
JSONRequest() { id = Value::null; }
|
|
|
|
|
void parse(const Value& valRequest);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void JSONRequest::parse(const Value& valRequest)
|
|
|
|
|
{
|
|
|
|
|
// Parse request
|
|
|
|
|
if (valRequest.type() != obj_type)
|
|
|
|
|
throw JSONRPCError(-32600, "Invalid Request object");
|
|
|
|
|
const Object& request = valRequest.get_obj();
|
|
|
|
|
|
|
|
|
|
// Parse id now so errors from here on will have the id
|
|
|
|
|
id = find_value(request, "id");
|
|
|
|
|
|
|
|
|
|
// Parse method
|
|
|
|
|
Value valMethod = find_value(request, "method");
|
|
|
|
|
if (valMethod.type() == null_type)
|
|
|
|
|
throw JSONRPCError(-32600, "Missing method");
|
|
|
|
|
if (valMethod.type() != str_type)
|
|
|
|
|
throw JSONRPCError(-32600, "Method must be a string");
|
|
|
|
|
strMethod = valMethod.get_str();
|
2012-08-03 01:12:55 +00:00
|
|
|
if (strMethod != "getwork" && strMethod != "getblocktemplate")
|
2012-06-27 13:47:02 -04:00
|
|
|
printf("ThreadRPCServer method=%s\n", strMethod.c_str());
|
|
|
|
|
|
|
|
|
|
// Parse params
|
|
|
|
|
Value valParams = find_value(request, "params");
|
|
|
|
|
if (valParams.type() == array_type)
|
|
|
|
|
params = valParams.get_array();
|
|
|
|
|
else if (valParams.type() == null_type)
|
|
|
|
|
params = Array();
|
|
|
|
|
else
|
|
|
|
|
throw JSONRPCError(-32600, "Params must be an array");
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-24 02:01:28 -04:00
|
|
|
static Object JSONRPCExecOne(const Value& req)
|
|
|
|
|
{
|
|
|
|
|
Object rpc_result;
|
|
|
|
|
|
|
|
|
|
JSONRequest jreq;
|
|
|
|
|
try {
|
|
|
|
|
jreq.parse(req);
|
|
|
|
|
|
|
|
|
|
Value result = tableRPC.execute(jreq.strMethod, jreq.params);
|
|
|
|
|
rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
|
|
|
|
|
}
|
|
|
|
|
catch (Object& objError)
|
|
|
|
|
{
|
|
|
|
|
rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception& e)
|
|
|
|
|
{
|
|
|
|
|
rpc_result = JSONRPCReplyObj(Value::null,
|
|
|
|
|
JSONRPCError(-32700, e.what()), jreq.id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rpc_result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static string JSONRPCExecBatch(const Array& vReq)
|
|
|
|
|
{
|
|
|
|
|
Array ret;
|
|
|
|
|
for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
|
|
|
|
|
ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
|
|
|
|
|
|
|
|
|
|
return write_string(Value(ret), false) + "\n";
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-28 18:34:11 +02:00
|
|
|
static CCriticalSection cs_THREAD_RPCHANDLER;
|
|
|
|
|
|
2012-04-14 20:35:58 -04:00
|
|
|
void ThreadRPCServer3(void* parg)
|
|
|
|
|
{
|
|
|
|
|
IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer3(parg));
|
2012-06-24 17:03:57 +02:00
|
|
|
|
|
|
|
|
// Make this thread recognisable as the RPC handler
|
|
|
|
|
RenameThread("bitcoin-rpchand");
|
|
|
|
|
|
2012-06-28 18:34:11 +02:00
|
|
|
{
|
|
|
|
|
LOCK(cs_THREAD_RPCHANDLER);
|
|
|
|
|
vnThreadsRunning[THREAD_RPCHANDLER]++;
|
|
|
|
|
}
|
2012-04-14 20:35:58 -04:00
|
|
|
AcceptedConnection *conn = (AcceptedConnection *) parg;
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2012-04-24 01:10:02 -04:00
|
|
|
bool fRun = true;
|
|
|
|
|
loop {
|
|
|
|
|
if (fShutdown || !fRun)
|
|
|
|
|
{
|
2011-08-10 15:07:46 +02:00
|
|
|
conn->close();
|
2012-04-24 01:10:02 -04:00
|
|
|
delete conn;
|
2012-06-28 18:34:11 +02:00
|
|
|
{
|
|
|
|
|
LOCK(cs_THREAD_RPCHANDLER);
|
|
|
|
|
--vnThreadsRunning[THREAD_RPCHANDLER];
|
|
|
|
|
}
|
2012-04-24 01:10:02 -04:00
|
|
|
return;
|
|
|
|
|
}
|
2011-05-14 20:10:21 +02:00
|
|
|
map<string, string> mapHeaders;
|
|
|
|
|
string strRequest;
|
|
|
|
|
|
2011-08-10 15:07:46 +02:00
|
|
|
ReadHTTP(conn->stream(), mapHeaders, strRequest);
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
// Check authorization
|
|
|
|
|
if (mapHeaders.count("authorization") == 0)
|
|
|
|
|
{
|
2011-08-10 15:07:46 +02:00
|
|
|
conn->stream() << HTTPReply(401, "", false) << std::flush;
|
2012-04-14 20:35:58 -04:00
|
|
|
break;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
if (!HTTPAuthorized(mapHeaders))
|
|
|
|
|
{
|
2011-08-10 15:07:46 +02:00
|
|
|
printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str());
|
2012-02-05 02:30:43 -05:00
|
|
|
/* Deter brute-forcing short passwords.
|
|
|
|
|
If this results in a DOS the user really
|
|
|
|
|
shouldn't have their RPC port exposed.*/
|
|
|
|
|
if (mapArgs["-rpcpassword"].size() < 20)
|
|
|
|
|
Sleep(250);
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2011-08-10 15:07:46 +02:00
|
|
|
conn->stream() << HTTPReply(401, "", false) << std::flush;
|
2012-04-14 20:35:58 -04:00
|
|
|
break;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
2012-04-24 01:10:02 -04:00
|
|
|
if (mapHeaders["connection"] == "close")
|
|
|
|
|
fRun = false;
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2012-06-27 13:47:02 -04:00
|
|
|
JSONRequest jreq;
|
2011-05-14 20:10:21 +02:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Parse request
|
|
|
|
|
Value valRequest;
|
2012-06-24 02:01:28 -04:00
|
|
|
if (!read_string(strRequest, valRequest))
|
2011-05-14 20:10:21 +02:00
|
|
|
throw JSONRPCError(-32700, "Parse error");
|
|
|
|
|
|
2012-06-24 02:01:28 -04:00
|
|
|
string strReply;
|
|
|
|
|
|
|
|
|
|
// singleton request
|
|
|
|
|
if (valRequest.type() == obj_type) {
|
|
|
|
|
jreq.parse(valRequest);
|
2012-06-27 13:47:02 -04:00
|
|
|
|
2012-06-24 02:01:28 -04:00
|
|
|
Value result = tableRPC.execute(jreq.strMethod, jreq.params);
|
2012-04-09 21:07:25 +02:00
|
|
|
|
2012-06-24 02:01:28 -04:00
|
|
|
// Send reply
|
|
|
|
|
strReply = JSONRPCReply(result, Value::null, jreq.id);
|
|
|
|
|
|
|
|
|
|
// array of requests
|
|
|
|
|
} else if (valRequest.type() == array_type)
|
|
|
|
|
strReply = JSONRPCExecBatch(valRequest.get_array());
|
|
|
|
|
else
|
|
|
|
|
throw JSONRPCError(-32700, "Top-level object parse error");
|
|
|
|
|
|
2011-08-10 15:07:46 +02:00
|
|
|
conn->stream() << HTTPReply(200, strReply, fRun) << std::flush;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
catch (Object& objError)
|
|
|
|
|
{
|
2012-06-27 13:47:02 -04:00
|
|
|
ErrorReply(conn->stream(), objError, jreq.id);
|
2012-04-14 20:35:58 -04:00
|
|
|
break;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
catch (std::exception& e)
|
|
|
|
|
{
|
2012-06-27 13:47:02 -04:00
|
|
|
ErrorReply(conn->stream(), JSONRPCError(-32700, e.what()), jreq.id);
|
2012-04-14 20:35:58 -04:00
|
|
|
break;
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
2012-04-24 01:10:02 -04:00
|
|
|
|
2012-04-14 20:35:58 -04:00
|
|
|
delete conn;
|
2012-06-28 18:34:11 +02:00
|
|
|
{
|
|
|
|
|
LOCK(cs_THREAD_RPCHANDLER);
|
|
|
|
|
vnThreadsRunning[THREAD_RPCHANDLER]--;
|
|
|
|
|
}
|
2011-05-14 20:10:21 +02:00
|
|
|
}
|
|
|
|
|
|
2012-04-09 21:07:25 +02:00
|
|
|
json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const
|
|
|
|
|
{
|
|
|
|
|
// Find method
|
|
|
|
|
const CRPCCommand *pcmd = tableRPC[strMethod];
|
|
|
|
|
if (!pcmd)
|
|
|
|
|
throw JSONRPCError(-32601, "Method not found");
|
2011-05-14 20:10:21 +02:00
|
|
|
|
2012-04-09 21:07:25 +02:00
|
|
|
// Observe safe mode
|
|
|
|
|
string strWarning = GetWarnings("rpc");
|
|
|
|
|
if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
|
|
|
|
|
!pcmd->okSafeMode)
|
|
|
|
|
throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Execute
|
|
|
|
|
Value result;
|
|
|
|
|
{
|
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
|
|
|
result = pcmd->actor(params, false);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception& e)
|
|
|
|
|
{
|
|
|
|
|
throw JSONRPCError(-1, e.what());
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
Object CallRPC(const string& strMethod, const Array& params)
|
|
|
|
|
{
|
|
|
|
|
if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
|
|
|
|
|
throw runtime_error(strprintf(
|
|
|
|
|
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
|
|
|
|
|
"If the file does not exist, create it with owner-readable-only file permissions."),
|
2012-04-09 23:50:56 +02:00
|
|
|
GetConfigFile().string().c_str()));
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
// Connect to localhost
|
|
|
|
|
bool fUseSSL = GetBoolArg("-rpcssl");
|
|
|
|
|
asio::io_service io_service;
|
|
|
|
|
ssl::context context(io_service, ssl::context::sslv23);
|
|
|
|
|
context.set_options(ssl::context::no_sslv2);
|
2011-08-10 15:07:46 +02:00
|
|
|
asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
|
|
|
|
|
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
|
|
|
|
|
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
|
2011-05-14 20:10:21 +02:00
|
|
|
if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
|
|
|
|
|
throw runtime_error("couldn't connect to server");
|
|
|
|
|
|
|
|
|
|
// HTTP basic authentication
|
|
|
|
|
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
|
|
|
|
|
map<string, string> mapRequestHeaders;
|
|
|
|
|
mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
|
|
|
|
|
|
|
|
|
|
// Send request
|
|
|
|
|
string strRequest = JSONRPCRequest(strMethod, params, 1);
|
|
|
|
|
string strPost = HTTPPost(strRequest, mapRequestHeaders);
|
|
|
|
|
stream << strPost << std::flush;
|
|
|
|
|
|
|
|
|
|
// Receive reply
|
|
|
|
|
map<string, string> mapHeaders;
|
|
|
|
|
string strReply;
|
|
|
|
|
int nStatus = ReadHTTP(stream, mapHeaders, strReply);
|
|
|
|
|
if (nStatus == 401)
|
|
|
|
|
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
|
|
|
|
else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
|
|
|
|
|
throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
|
|
|
|
|
else if (strReply.empty())
|
|
|
|
|
throw runtime_error("no response from server");
|
|
|
|
|
|
|
|
|
|
// Parse reply
|
|
|
|
|
Value valReply;
|
|
|
|
|
if (!read_string(strReply, valReply))
|
|
|
|
|
throw runtime_error("couldn't parse reply from server");
|
|
|
|
|
const Object& reply = valReply.get_obj();
|
|
|
|
|
if (reply.empty())
|
|
|
|
|
throw runtime_error("expected reply to have result, error and id properties");
|
|
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
void ConvertTo(Value& value)
|
|
|
|
|
{
|
|
|
|
|
if (value.type() == str_type)
|
|
|
|
|
{
|
|
|
|
|
// reinterpret string as unquoted json value
|
|
|
|
|
Value value2;
|
2012-07-17 12:02:31 -04:00
|
|
|
string strJSON = value.get_str();
|
|
|
|
|
if (!read_string(strJSON, value2))
|
|
|
|
|
throw runtime_error(string("Error parsing JSON:")+strJSON);
|
2011-05-14 20:10:21 +02:00
|
|
|
value = value2.get_value<T>();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = value.get_value<T>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-09 21:07:25 +02:00
|
|
|
// Convert strings to command-specific RPC representation
|
|
|
|
|
Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
|
|
|
|
|
{
|
|
|
|
|
Array params;
|
|
|
|
|
BOOST_FOREACH(const std::string ¶m, strParams)
|
|
|
|
|
params.push_back(param);
|
|
|
|
|
|
|
|
|
|
int n = params.size();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Special case non-string parameter types
|
|
|
|
|
//
|
|
|
|
|
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
|
|
|
|
|
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
|
|
|
|
|
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
|
|
|
|
|
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
|
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
|
|
|
|
|
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
|
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
|
|
|
|
|
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
|
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
|
|
|
|
|
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
|
|
|
|
if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
|
|
|
|
|
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
|
|
|
|
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
|
|
|
|
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
|
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
2012-08-03 01:12:55 +00:00
|
|
|
if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
|
2012-04-09 21:07:25 +02:00
|
|
|
if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
2012-05-31 16:09:31 -04:00
|
|
|
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
|
|
|
|
|
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
|
|
|
|
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
|
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
|
2012-05-31 16:01:16 -04:00
|
|
|
if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
|
if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
|
|
|
|
if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
|
|
|
|
|
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
|
|
|
|
|
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1]);
|
|
|
|
|
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2]);
|
2012-05-31 16:09:31 -04:00
|
|
|
|
2012-04-09 21:07:25 +02:00
|
|
|
return params;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 20:10:21 +02:00
|
|
|
int CommandLineRPC(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
string strPrint;
|
|
|
|
|
int nRet = 0;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Skip switches
|
|
|
|
|
while (argc > 1 && IsSwitchChar(argv[1][0]))
|
|
|
|
|
{
|
|
|
|
|
argc--;
|
|
|
|
|
argv++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Method
|
|
|
|
|
if (argc < 2)
|
|
|
|
|
throw runtime_error("too few parameters");
|
|
|
|
|
string strMethod = argv[1];
|
|
|
|
|
|
|
|
|
|
// Parameters default to strings
|
2012-04-09 21:07:25 +02:00
|
|
|
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
|
|
|
|
Array params = RPCConvertValues(strMethod, strParams);
|
2011-05-14 20:10:21 +02:00
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
Object reply = CallRPC(strMethod, params);
|
|
|
|
|
|
|
|
|
|
// Parse reply
|
|
|
|
|
const Value& result = find_value(reply, "result");
|
|
|
|
|
const Value& error = find_value(reply, "error");
|
|
|
|
|
|
|
|
|
|
if (error.type() != null_type)
|
|
|
|
|
{
|
|
|
|
|
// Error
|
|
|
|
|
strPrint = "error: " + write_string(error, false);
|
|
|
|
|
int code = find_value(error.get_obj(), "code").get_int();
|
|
|
|
|
nRet = abs(code);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Result
|
|
|
|
|
if (result.type() == null_type)
|
|
|
|
|
strPrint = "";
|
|
|
|
|
else if (result.type() == str_type)
|
|
|
|
|
strPrint = result.get_str();
|
|
|
|
|
else
|
|
|
|
|
strPrint = write_string(result, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception& e)
|
|
|
|
|
{
|
|
|
|
|
strPrint = string("error: ") + e.what();
|
|
|
|
|
nRet = 87;
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
PrintException(NULL, "CommandLineRPC()");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strPrint != "")
|
|
|
|
|
{
|
|
|
|
|
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
|
|
|
|
}
|
|
|
|
|
return nRet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
#ifdef _MSC_VER
|
2012-07-26 00:48:39 +00:00
|
|
|
// Turn off Microsoft heap dump noise
|
2011-05-14 20:10:21 +02:00
|
|
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
|
|
|
|
|
_CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
|
|
|
|
|
#endif
|
|
|
|
|
setbuf(stdin, NULL);
|
|
|
|
|
setbuf(stdout, NULL);
|
|
|
|
|
setbuf(stderr, NULL);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (argc >= 2 && string(argv[1]) == "-server")
|
|
|
|
|
{
|
|
|
|
|
printf("server ready\n");
|
|
|
|
|
ThreadRPCServer(NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return CommandLineRPC(argc, argv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception& e) {
|
|
|
|
|
PrintException(&e, "main()");
|
|
|
|
|
} catch (...) {
|
|
|
|
|
PrintException(NULL, "main()");
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2012-04-21 01:37:34 +02:00
|
|
|
|
|
|
|
|
const CRPCTable tableRPC;
|