mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-28 21:34:25 +01:00
Start the RPC server before doing all the (expensive) startup initialisations like loading the block index. Until the node is ready, return all calls immediately with a new error signalling "in warmup" with an appropriate status message (similar to the init message). This is useful for RPC clients to know that the server is there (e. g., they don't have to start it) but not yet available. It is used in Namecoin and Huntercoin already for some time, and there exists a UI hooked onto the RPC interface that actively uses this to its advantage.
168 lines
7.6 KiB
C++
168 lines
7.6 KiB
C++
// Copyright (c) 2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2014 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_RPCPROTOCOL_H
|
|
#define BITCOIN_RPCPROTOCOL_H
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <stdint.h>
|
|
#include <string>
|
|
#include <boost/iostreams/concepts.hpp>
|
|
#include <boost/iostreams/stream.hpp>
|
|
#include <boost/asio.hpp>
|
|
#include <boost/asio/ssl.hpp>
|
|
|
|
#include "json/json_spirit_reader_template.h"
|
|
#include "json/json_spirit_utils.h"
|
|
#include "json/json_spirit_writer_template.h"
|
|
|
|
// HTTP status codes
|
|
enum HTTPStatusCode
|
|
{
|
|
HTTP_OK = 200,
|
|
HTTP_BAD_REQUEST = 400,
|
|
HTTP_UNAUTHORIZED = 401,
|
|
HTTP_FORBIDDEN = 403,
|
|
HTTP_NOT_FOUND = 404,
|
|
HTTP_INTERNAL_SERVER_ERROR = 500,
|
|
};
|
|
|
|
// Bitcoin RPC error codes
|
|
enum RPCErrorCode
|
|
{
|
|
// Standard JSON-RPC 2.0 errors
|
|
RPC_INVALID_REQUEST = -32600,
|
|
RPC_METHOD_NOT_FOUND = -32601,
|
|
RPC_INVALID_PARAMS = -32602,
|
|
RPC_INTERNAL_ERROR = -32603,
|
|
RPC_PARSE_ERROR = -32700,
|
|
|
|
// General application defined errors
|
|
RPC_MISC_ERROR = -1, // std::exception thrown in command handling
|
|
RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode
|
|
RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter
|
|
RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key
|
|
RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation
|
|
RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter
|
|
RPC_DATABASE_ERROR = -20, // Database error
|
|
RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format
|
|
RPC_VERIFY_ERROR = -25, // General error during transaction or block submission
|
|
RPC_VERIFY_REJECTED = -26, // Transaction or block was rejected by network rules
|
|
RPC_VERIFY_ALREADY_IN_CHAIN = -27, // Transaction already in chain
|
|
RPC_IN_WARMUP = -28, // Client still warming up
|
|
|
|
// Aliases for backward compatibility
|
|
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,
|
|
RPC_TRANSACTION_REJECTED = RPC_VERIFY_REJECTED,
|
|
RPC_TRANSACTION_ALREADY_IN_CHAIN= RPC_VERIFY_ALREADY_IN_CHAIN,
|
|
|
|
// P2P client errors
|
|
RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected
|
|
RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks
|
|
RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added
|
|
RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before
|
|
|
|
// Wallet errors
|
|
RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.)
|
|
RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account
|
|
RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name
|
|
RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first
|
|
RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first
|
|
RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect
|
|
RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
|
|
RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet
|
|
RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked
|
|
};
|
|
|
|
//
|
|
// IOStream device that speaks SSL but can also speak non-SSL
|
|
//
|
|
template <typename Protocol>
|
|
class SSLIOStreamDevice : public boost::iostreams::device<boost::iostreams::bidirectional> {
|
|
public:
|
|
SSLIOStreamDevice(boost::asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
|
|
{
|
|
fUseSSL = fUseSSLIn;
|
|
fNeedHandshake = fUseSSLIn;
|
|
}
|
|
|
|
void handshake(boost::asio::ssl::stream_base::handshake_type role)
|
|
{
|
|
if (!fNeedHandshake) return;
|
|
fNeedHandshake = false;
|
|
stream.handshake(role);
|
|
}
|
|
std::streamsize read(char* s, std::streamsize n)
|
|
{
|
|
handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first
|
|
if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n));
|
|
return stream.next_layer().read_some(boost::asio::buffer(s, n));
|
|
}
|
|
std::streamsize write(const char* s, std::streamsize n)
|
|
{
|
|
handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first
|
|
if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n));
|
|
return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n));
|
|
}
|
|
bool connect(const std::string& server, const std::string& port)
|
|
{
|
|
using namespace boost::asio::ip;
|
|
tcp::resolver resolver(stream.get_io_service());
|
|
tcp::resolver::iterator endpoint_iterator;
|
|
#if BOOST_VERSION >= 104300
|
|
try {
|
|
#endif
|
|
// The default query (flags address_configured) tries IPv6 if
|
|
// non-localhost IPv6 configured, and IPv4 if non-localhost IPv4
|
|
// configured.
|
|
tcp::resolver::query query(server.c_str(), port.c_str());
|
|
endpoint_iterator = resolver.resolve(query);
|
|
#if BOOST_VERSION >= 104300
|
|
} catch(boost::system::system_error &e)
|
|
{
|
|
// If we at first don't succeed, try blanket lookup (IPv4+IPv6 independent of configured interfaces)
|
|
tcp::resolver::query query(server.c_str(), port.c_str(), resolver_query_base::flags());
|
|
endpoint_iterator = resolver.resolve(query);
|
|
}
|
|
#endif
|
|
boost::system::error_code error = boost::asio::error::host_not_found;
|
|
tcp::resolver::iterator end;
|
|
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;
|
|
boost::asio::ssl::stream<typename Protocol::socket>& stream;
|
|
};
|
|
|
|
std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders);
|
|
std::string HTTPError(int nStatus, bool keepalive,
|
|
bool headerOnly = false);
|
|
std::string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength,
|
|
const char *contentType = "application/json");
|
|
std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive,
|
|
bool headerOnly = false,
|
|
const char *contentType = "application/json");
|
|
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
|
std::string& http_method, std::string& http_uri);
|
|
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto);
|
|
int ReadHTTPHeaders(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet);
|
|
int ReadHTTPMessage(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
|
|
std::string& strMessageRet, int nProto, size_t max_size);
|
|
std::string JSONRPCRequest(const std::string& strMethod, const json_spirit::Array& params, const json_spirit::Value& id);
|
|
json_spirit::Object JSONRPCReplyObj(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id);
|
|
std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id);
|
|
json_spirit::Object JSONRPCError(int code, const std::string& message);
|
|
|
|
#endif // BITCOIN_RPCPROTOCOL_H
|