mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-10 10:52:31 -05:00
![Gavin Andresen](/assets/img/avatar_default.png)
New RPC methods: return an estimate of the fee (or priority) a transaction needs to be likely to confirm in a given number of blocks. Mike Hearn created the first version of this method for estimating fees. It works as follows: For transactions that took 1 to N (I picked N=25) blocks to confirm, keep N buckets with at most 100 entries in each recording the fees-per-kilobyte paid by those transactions. (separate buckets are kept for transactions that confirmed because they are high-priority) The buckets are filled as blocks are found, and are saved/restored in a new fee_estiamtes.dat file in the data directory. A few variations on Mike's initial scheme: To estimate the fee needed for a transaction to confirm in X buckets, all of the samples in all of the buckets are used and a median of all of the data is used to make the estimate. For example, imagine 25 buckets each containing the full 100 entries. Those 2,500 samples are sorted, and the estimate of the fee needed to confirm in the very next block is the 50'th-highest-fee-entry in that sorted list; the estimate of the fee needed to confirm in the next two blocks is the 150'th-highest-fee-entry, etc. That algorithm has the nice property that estimates of how much fee you need to pay to get confirmed in block N will always be greater than or equal to the estimate for block N+1. It would clearly be wrong to say "pay 11 uBTC and you'll get confirmed in 3 blocks, but pay 12 uBTC and it will take LONGER". A single block will not contribute more than 10 entries to any one bucket, so a single miner and a large block cannot overwhelm the estimates.
282 lines
12 KiB
C++
282 lines
12 KiB
C++
// Copyright (c) 2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "rpcclient.h"
|
|
|
|
#include "rpcprotocol.h"
|
|
#include "util.h"
|
|
#include "ui_interface.h"
|
|
#include "chainparams.h" // for Params().RPCPort()
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/asio.hpp>
|
|
#include <boost/asio/ssl.hpp>
|
|
#include <boost/bind.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/iostreams/concepts.hpp>
|
|
#include <boost/iostreams/stream.hpp>
|
|
#include <boost/shared_ptr.hpp>
|
|
#include "json/json_spirit_writer_template.h"
|
|
|
|
using namespace std;
|
|
using namespace boost;
|
|
using namespace boost::asio;
|
|
using namespace json_spirit;
|
|
|
|
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."),
|
|
GetConfigFile().string().c_str()));
|
|
|
|
// Connect to localhost
|
|
bool fUseSSL = GetBoolArg("-rpcssl", false);
|
|
asio::io_service io_service;
|
|
ssl::context context(io_service, ssl::context::sslv23);
|
|
context.set_options(ssl::context::no_sslv2);
|
|
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);
|
|
|
|
bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started
|
|
do {
|
|
bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort())));
|
|
if (fConnected) break;
|
|
if (fWait)
|
|
MilliSleep(1000);
|
|
else
|
|
throw runtime_error("couldn't connect to server");
|
|
} while (fWait);
|
|
|
|
// 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 HTTP reply status
|
|
int nProto = 0;
|
|
int nStatus = ReadHTTPStatus(stream, nProto);
|
|
|
|
// Receive HTTP reply message headers and body
|
|
map<string, string> mapHeaders;
|
|
string strReply;
|
|
ReadHTTPMessage(stream, mapHeaders, strReply, nProto);
|
|
|
|
if (nStatus == HTTP_UNAUTHORIZED)
|
|
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
|
else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
|
|
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, bool fAllowNull=false)
|
|
{
|
|
if (fAllowNull && value.type() == null_type)
|
|
return;
|
|
if (value.type() == str_type)
|
|
{
|
|
// reinterpret string as unquoted json value
|
|
Value value2;
|
|
string strJSON = value.get_str();
|
|
if (!read_string(strJSON, value2))
|
|
throw runtime_error(string("Error parsing JSON:")+strJSON);
|
|
ConvertTo<T>(value2, fAllowNull);
|
|
value = value2;
|
|
}
|
|
else
|
|
{
|
|
value = value.get_value<T>();
|
|
}
|
|
}
|
|
|
|
// 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 == "stop" && n > 0) ConvertTo<bool>(params[0]);
|
|
if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]);
|
|
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
|
|
if (strMethod == "setgenerate" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<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<int64_t>(params[1]);
|
|
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
|
|
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
|
|
if (strMethod == "getbalance" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "getblockhash" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
|
|
if (strMethod == "move" && n > 3) ConvertTo<int64_t>(params[3]);
|
|
if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
|
|
if (strMethod == "sendfrom" && n > 3) ConvertTo<int64_t>(params[3]);
|
|
if (strMethod == "listtransactions" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "listtransactions" && n > 2) ConvertTo<int64_t>(params[2]);
|
|
if (strMethod == "listaccounts" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
|
|
if (strMethod == "listsinceblock" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
|
|
if (strMethod == "sendmany" && n > 2) ConvertTo<int64_t>(params[2]);
|
|
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
|
|
if (strMethod == "createmultisig" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]);
|
|
if (strMethod == "listunspent" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "listunspent" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
|
|
if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]);
|
|
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<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], true);
|
|
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
|
|
if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true);
|
|
if (strMethod == "gettxout" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
|
|
if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
|
|
if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
|
|
if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);
|
|
if (strMethod == "verifychain" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "verifychain" && n > 1) ConvertTo<int64_t>(params[1]);
|
|
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<int64_t>(params[0]);
|
|
if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]);
|
|
if (strMethod == "estimatefee" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
if (strMethod == "estimatepriority" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
|
|
|
return params;
|
|
}
|
|
|
|
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
|
|
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
|
Array params = RPCConvertValues(strMethod, strParams);
|
|
|
|
// 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 (boost::thread_interrupted) {
|
|
throw;
|
|
}
|
|
catch (std::exception& e) {
|
|
strPrint = string("error: ") + e.what();
|
|
nRet = abs(RPC_MISC_ERROR);
|
|
}
|
|
catch (...) {
|
|
PrintExceptionContinue(NULL, "CommandLineRPC()");
|
|
throw;
|
|
}
|
|
|
|
if (strPrint != "")
|
|
{
|
|
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
std::string HelpMessageCli(bool mainProgram)
|
|
{
|
|
string strUsage;
|
|
if(mainProgram)
|
|
{
|
|
strUsage += _("Options:") + "\n";
|
|
strUsage += " -? " + _("This help message") + "\n";
|
|
strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n";
|
|
strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
|
|
strUsage += " -testnet " + _("Use the test network") + "\n";
|
|
strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be "
|
|
"solved instantly. This is intended for regression testing tools and app development.") + "\n";
|
|
} else {
|
|
strUsage += _("RPC client options:") + "\n";
|
|
}
|
|
|
|
strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n";
|
|
strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n";
|
|
strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n";
|
|
|
|
if(mainProgram)
|
|
{
|
|
strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n";
|
|
strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n";
|
|
|
|
strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
|
|
strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n";
|
|
}
|
|
|
|
return strUsage;
|
|
}
|
|
|