Reorganize source file hierarchy:

* Rename unity files
* Move some modules to new subdirectories
* Remove obsolete Visual Studio project files
* Remove obsolete coding style and TODO list
This commit is contained in:
Vinnie Falco
2014-06-03 14:48:34 -07:00
parent 39a387b54c
commit 4f1d1d2a8a
774 changed files with 6924 additions and 10355 deletions

View File

@@ -0,0 +1,787 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/module/core/text/LexicalCast.h>
namespace ripple {
//
// TODO: Check permissions on config file before using it.
//
// Fees are in XRP.
#define DEFAULT_FEE_DEFAULT 10
#define DEFAULT_FEE_ACCOUNT_RESERVE 200*SYSTEM_CURRENCY_PARTS
#define DEFAULT_FEE_OWNER_RESERVE 50*SYSTEM_CURRENCY_PARTS
#define DEFAULT_FEE_NICKNAME_CREATE 1000
#define DEFAULT_FEE_OFFER DEFAULT_FEE_DEFAULT
#define DEFAULT_FEE_OPERATION 1
/** Parses a set of strings into IP::Endpoint
Strings which fail to parse are not included in the output. If a stream is
provided, human readable diagnostic error messages are written for each
failed parse.
@param out An OutputSequence to store the IP::Endpoint list
@param first The begining of the string input sequence
@param last The one-past-the-end of the string input sequence
*/
template <class OutputSequence, class InputIterator>
void parseAddresses (OutputSequence& out, InputIterator first, InputIterator last,
beast::Journal::Stream stream = beast::Journal::Stream ())
{
while (first != last)
{
auto const str (*first);
++first;
{
beast::IP::Endpoint const addr (beast::IP::Endpoint::from_string (str));
if (! is_unspecified (addr))
{
out.push_back (addr);
continue;
}
}
{
beast::IP::Endpoint const addr (beast::IP::Endpoint::from_string_altform (str));
if (! is_unspecified (addr))
{
out.push_back (addr);
continue;
}
}
if (stream) stream <<
"Config: \"" << str << "\" is not a valid IP address.";
}
}
//------------------------------------------------------------------------------
Config::Config ()
: m_rpcPort (5001)
{
//--------------------------------------------------------------------------
//
// VFALCO NOTE Clean member area
//
peerListeningPort = SYSTEM_PEER_PORT;
peerPROXYListeningPort = 0;
//
//
//
//--------------------------------------------------------------------------
//
// Defaults
//
NETWORK_START_TIME = 1319844908;
RPC_SECURE = 0;
WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT;
WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT;
WEBSOCKET_PUBLIC_SECURE = 1;
WEBSOCKET_PROXY_PORT = 0;
WEBSOCKET_PROXY_SECURE = 1;
WEBSOCKET_SECURE = 0;
WEBSOCKET_PING_FREQ = (5 * 60);
NUMBER_CONNECTIONS = 30;
// a new ledger every minute
LEDGER_SECONDS = 60;
LEDGER_CREATOR = false;
RPC_ALLOW_REMOTE = false;
RPC_ADMIN_ALLOW.push_back (beast::IP::Endpoint::from_string("127.0.0.1"));
PEER_SSL_CIPHER_LIST = DEFAULT_PEER_SSL_CIPHER_LIST;
PEER_SCAN_INTERVAL_MIN = DEFAULT_PEER_SCAN_INTERVAL_MIN;
PEER_START_MAX = DEFAULT_PEER_START_MAX;
PEER_CONNECT_LOW_WATER = DEFAULT_PEER_CONNECT_LOW_WATER;
PEER_PRIVATE = false;
PEERS_MAX = 0; // indicates "use default"
TRANSACTION_FEE_BASE = DEFAULT_FEE_DEFAULT;
NETWORK_QUORUM = 0; // Don't need to see other nodes
VALIDATION_QUORUM = 1; // Only need one node to vouch
FEE_ACCOUNT_RESERVE = DEFAULT_FEE_ACCOUNT_RESERVE;
FEE_OWNER_RESERVE = DEFAULT_FEE_OWNER_RESERVE;
FEE_NICKNAME_CREATE = DEFAULT_FEE_NICKNAME_CREATE;
FEE_OFFER = DEFAULT_FEE_OFFER;
FEE_DEFAULT = DEFAULT_FEE_DEFAULT;
FEE_CONTRACT_OPERATION = DEFAULT_FEE_OPERATION;
LEDGER_HISTORY = 256;
FETCH_DEPTH = 1000000000;
PATH_SEARCH_OLD = DEFAULT_PATH_SEARCH_OLD;
PATH_SEARCH = DEFAULT_PATH_SEARCH;
PATH_SEARCH_FAST = DEFAULT_PATH_SEARCH_FAST;
PATH_SEARCH_MAX = DEFAULT_PATH_SEARCH_MAX;
ACCOUNT_PROBE_MAX = 10;
VALIDATORS_SITE = "";
SSL_VERIFY = true;
ELB_SUPPORT = false;
RUN_STANDALONE = false;
doImport = false;
START_UP = NORMAL;
}
void Config::setup (const std::string& strConf, bool bQuiet)
{
boost::system::error_code ec;
std::string strDbPath, strConfFile;
//
// Determine the config and data directories.
// If the config file is found in the current working directory, use the current working directory as the config directory and
// that with "db" as the data directory.
//
QUIET = bQuiet;
NODE_SIZE = 0;
strDbPath = Helpers::getDatabaseDirName ();
strConfFile = strConf.empty () ? Helpers::getConfigFileName () : strConf;
VALIDATORS_BASE = Helpers::getValidatorsFileName ();
VALIDATORS_URI = boost::str (boost::format ("/%s") % VALIDATORS_BASE);
SIGN_TRANSACTION = HashPrefix::txSign;
SIGN_VALIDATION = HashPrefix::validation;
SIGN_PROPOSAL = HashPrefix::proposal;
if (!strConf.empty ())
{
// --conf=<path> : everything is relative that file.
CONFIG_FILE = strConfFile;
CONFIG_DIR = boost::filesystem::absolute (CONFIG_FILE);
CONFIG_DIR.remove_filename ();
DATA_DIR = CONFIG_DIR / strDbPath;
}
else
{
CONFIG_DIR = boost::filesystem::current_path ();
CONFIG_FILE = CONFIG_DIR / strConfFile;
DATA_DIR = CONFIG_DIR / strDbPath;
if (exists (CONFIG_FILE)
// Can we figure out XDG dirs?
|| (!getenv ("HOME") && (!getenv ("XDG_CONFIG_HOME") || !getenv ("XDG_DATA_HOME"))))
{
// Current working directory is fine, put dbs in a subdir.
nothing ();
}
else
{
// Construct XDG config and data home.
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
std::string strHome = strGetEnv ("HOME");
std::string strXdgConfigHome = strGetEnv ("XDG_CONFIG_HOME");
std::string strXdgDataHome = strGetEnv ("XDG_DATA_HOME");
if (strXdgConfigHome.empty ())
{
// $XDG_CONFIG_HOME was not set, use default based on $HOME.
strXdgConfigHome = boost::str (boost::format ("%s/.config") % strHome);
}
if (strXdgDataHome.empty ())
{
// $XDG_DATA_HOME was not set, use default based on $HOME.
strXdgDataHome = boost::str (boost::format ("%s/.local/share") % strHome);
}
CONFIG_DIR = boost::str (boost::format ("%s/" SYSTEM_NAME) % strXdgConfigHome);
CONFIG_FILE = CONFIG_DIR / strConfFile;
DATA_DIR = boost::str (boost::format ("%s/" SYSTEM_NAME) % strXdgDataHome);
boost::filesystem::create_directories (CONFIG_DIR, ec);
if (ec)
throw std::runtime_error (boost::str (boost::format ("Can not create %s") % CONFIG_DIR));
}
}
HTTPClient::initializeSSLContext();
// Update default values
load ();
// Log::out() << "CONFIG FILE: " << CONFIG_FILE;
// Log::out() << "CONFIG DIR: " << CONFIG_DIR;
// Log::out() << "DATA DIR: " << DATA_DIR;
boost::filesystem::create_directories (DATA_DIR, ec);
if (ec)
throw std::runtime_error (boost::str (boost::format ("Can not create %s") % DATA_DIR));
// Create the new unified database
m_moduleDbPath = getDatabaseDir();
// This code is temporarily disabled, and modules will fall back to using
// per-module databases (e.g. "peerfinder.sqlite") under the module db path
//
//if (m_moduleDbPath.isDirectory ())
// m_moduleDbPath = m_moduleDbPath.getChildFile("rippled.sqlite");
}
void Config::load ()
{
if (!QUIET)
Log::out() << "Loading: " << CONFIG_FILE;
std::ifstream ifsConfig (CONFIG_FILE.c_str (), std::ios::in);
if (!ifsConfig)
{
Log::out() << "Failed to open '" << CONFIG_FILE << "'.";
}
else
{
std::string strConfigFile;
strConfigFile.assign ((std::istreambuf_iterator<char> (ifsConfig)),
std::istreambuf_iterator<char> ());
if (ifsConfig.bad ())
{
Log::out() << "Failed to read '" << CONFIG_FILE << "'.";
}
else
{
Section secConfig = ParseSection (strConfigFile, true);
std::string strTemp;
// XXX Leak
Section::mapped_type* smtTmp;
smtTmp = SectionEntries (secConfig, SECTION_VALIDATORS);
if (smtTmp)
{
validators = *smtTmp;
}
smtTmp = SectionEntries (secConfig, SECTION_CLUSTER_NODES);
if (smtTmp)
{
CLUSTER_NODES = *smtTmp;
}
smtTmp = SectionEntries (secConfig, SECTION_IPS);
if (smtTmp)
{
IPS = *smtTmp;
}
smtTmp = SectionEntries (secConfig, SECTION_IPS_FIXED);
if (smtTmp)
{
IPS_FIXED = *smtTmp;
}
smtTmp = SectionEntries (secConfig, SECTION_SNTP);
if (smtTmp)
{
SNTP_SERVERS = *smtTmp;
}
smtTmp = SectionEntries (secConfig, SECTION_RPC_STARTUP);
if (smtTmp)
{
RPC_STARTUP = Json::arrayValue;
BOOST_FOREACH (const std::string & strJson, *smtTmp)
{
Json::Reader jrReader;
Json::Value jvCommand;
if (!jrReader.parse (strJson, jvCommand))
throw std::runtime_error (boost::str (boost::format ("Couldn't parse [" SECTION_RPC_STARTUP "] command: %s") % strJson));
RPC_STARTUP.append (jvCommand);
}
}
if (SectionSingleB (secConfig, SECTION_DATABASE_PATH, DATABASE_PATH))
DATA_DIR = DATABASE_PATH;
(void) SectionSingleB (secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE);
(void) SectionSingleB (secConfig, SECTION_PEER_IP, PEER_IP);
if (SectionSingleB (secConfig, SECTION_PEER_PRIVATE, strTemp))
PEER_PRIVATE = beast::lexicalCastThrow <bool> (strTemp);
if (SectionSingleB (secConfig, SECTION_PEERS_MAX, strTemp))
PEERS_MAX = beast::lexicalCastThrow <int> (strTemp);
smtTmp = SectionEntries (secConfig, SECTION_RPC_ADMIN_ALLOW);
if (smtTmp)
{
std::vector<beast::IP::Endpoint> parsedAddresses;
//parseAddresses<std::vector<beast::IP::Endpoint>, std::vector<std::string>::const_iterator>
// (parsedAddresses, (*smtTmp).cbegin(), (*smtTmp).cend());
parseAddresses (parsedAddresses, (*smtTmp).cbegin(), (*smtTmp).cend());
RPC_ADMIN_ALLOW.insert (RPC_ADMIN_ALLOW.end(),
parsedAddresses.cbegin (), parsedAddresses.cend ());
}
(void) SectionSingleB (secConfig, SECTION_RPC_ADMIN_PASSWORD, RPC_ADMIN_PASSWORD);
(void) SectionSingleB (secConfig, SECTION_RPC_ADMIN_USER, RPC_ADMIN_USER);
(void) SectionSingleB (secConfig, SECTION_RPC_IP, m_rpcIP);
(void) SectionSingleB (secConfig, SECTION_RPC_PASSWORD, RPC_PASSWORD);
(void) SectionSingleB (secConfig, SECTION_RPC_USER, RPC_USER);
insightSettings = parseKeyValueSection (secConfig, SECTION_INSIGHT);
//---------------------------------------
//
// VFALCO BEGIN CLEAN
//
nodeDatabase = parseKeyValueSection (
secConfig, ConfigSection::nodeDatabase ());
ephemeralNodeDatabase = parseKeyValueSection (
secConfig, ConfigSection::tempNodeDatabase ());
importNodeDatabase = parseKeyValueSection (
secConfig, ConfigSection::importNodeDatabase ());
if (SectionSingleB (secConfig, SECTION_PEER_PORT, strTemp))
peerListeningPort = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_PEER_PROXY_PORT, strTemp))
{
peerPROXYListeningPort = beast::lexicalCastThrow <int> (strTemp);
if (peerPROXYListeningPort != 0 && peerPROXYListeningPort == peerListeningPort)
throw std::runtime_error ("Peer and proxy listening ports can't be the same.");
}
else
{
peerPROXYListeningPort = 0;
}
//
// VFALCO END CLEAN
//
//---------------------------------------
if (SectionSingleB (secConfig, SECTION_RPC_PORT, strTemp))
m_rpcPort = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, "ledger_creator" , strTemp))
LEDGER_CREATOR = beast::lexicalCastThrow <bool> (strTemp);
if (SectionSingleB (secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp))
RPC_ALLOW_REMOTE = beast::lexicalCastThrow <bool> (strTemp);
if (SectionSingleB (secConfig, SECTION_NODE_SIZE, strTemp))
{
if (strTemp == "tiny")
NODE_SIZE = 0;
else if (strTemp == "small")
NODE_SIZE = 1;
else if (strTemp == "medium")
NODE_SIZE = 2;
else if (strTemp == "large")
NODE_SIZE = 3;
else if (strTemp == "huge")
NODE_SIZE = 4;
else
{
NODE_SIZE = beast::lexicalCastThrow <int> (strTemp);
if (NODE_SIZE < 0)
NODE_SIZE = 0;
else if (NODE_SIZE > 4)
NODE_SIZE = 4;
}
}
if (SectionSingleB (secConfig, SECTION_ELB_SUPPORT, strTemp))
ELB_SUPPORT = beast::lexicalCastThrow <bool> (strTemp);
(void) SectionSingleB (secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PORT, strTemp))
WEBSOCKET_PORT = beast::lexicalCastThrow <int> (strTemp);
(void) SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp))
WEBSOCKET_PUBLIC_PORT = beast::lexicalCastThrow <int> (strTemp);
(void) SectionSingleB (secConfig, SECTION_WEBSOCKET_PROXY_IP, WEBSOCKET_PROXY_IP);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PROXY_PORT, strTemp))
WEBSOCKET_PROXY_PORT = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_SECURE, strTemp))
WEBSOCKET_SECURE = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_SECURE, strTemp))
WEBSOCKET_PUBLIC_SECURE = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PROXY_SECURE, strTemp))
WEBSOCKET_PROXY_SECURE = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PING_FREQ, strTemp))
WEBSOCKET_PING_FREQ = beast::lexicalCastThrow <int> (strTemp);
SectionSingleB (secConfig, SECTION_WEBSOCKET_SSL_CERT, WEBSOCKET_SSL_CERT);
SectionSingleB (secConfig, SECTION_WEBSOCKET_SSL_CHAIN, WEBSOCKET_SSL_CHAIN);
SectionSingleB (secConfig, SECTION_WEBSOCKET_SSL_KEY, WEBSOCKET_SSL_KEY);
if (SectionSingleB (secConfig, SECTION_RPC_SECURE, strTemp))
RPC_SECURE = beast::lexicalCastThrow <int> (strTemp);
SectionSingleB (secConfig, SECTION_RPC_SSL_CERT, RPC_SSL_CERT);
SectionSingleB (secConfig, SECTION_RPC_SSL_CHAIN, RPC_SSL_CHAIN);
SectionSingleB (secConfig, SECTION_RPC_SSL_KEY, RPC_SSL_KEY);
SectionSingleB (secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE);
SectionSingleB (secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR);
if (SectionSingleB (secConfig, SECTION_SSL_VERIFY, strTemp))
SSL_VERIFY = beast::lexicalCastThrow <bool> (strTemp);
if (SectionSingleB (secConfig, SECTION_VALIDATION_SEED, strTemp))
{
VALIDATION_SEED.setSeedGeneric (strTemp);
if (VALIDATION_SEED.isValid ())
{
VALIDATION_PUB = RippleAddress::createNodePublic (VALIDATION_SEED);
VALIDATION_PRIV = RippleAddress::createNodePrivate (VALIDATION_SEED);
}
}
if (SectionSingleB (secConfig, SECTION_NODE_SEED, strTemp))
{
NODE_SEED.setSeedGeneric (strTemp);
if (NODE_SEED.isValid ())
{
NODE_PUB = RippleAddress::createNodePublic (NODE_SEED);
NODE_PRIV = RippleAddress::createNodePrivate (NODE_SEED);
}
}
(void) SectionSingleB (secConfig, SECTION_PEER_SSL_CIPHER_LIST, PEER_SSL_CIPHER_LIST);
if (SectionSingleB (secConfig, SECTION_PEER_SCAN_INTERVAL_MIN, strTemp))
// Minimum for min is 60 seconds.
PEER_SCAN_INTERVAL_MIN = std::max (60, beast::lexicalCastThrow <int> (strTemp));
if (SectionSingleB (secConfig, SECTION_PEER_START_MAX, strTemp))
PEER_START_MAX = std::max (1, beast::lexicalCastThrow <int> (strTemp));
if (SectionSingleB (secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp))
PEER_CONNECT_LOW_WATER = std::max (1, beast::lexicalCastThrow <int> (strTemp));
if (SectionSingleB (secConfig, SECTION_NETWORK_QUORUM, strTemp))
NETWORK_QUORUM = std::max (0, beast::lexicalCastThrow <int> (strTemp));
if (SectionSingleB (secConfig, SECTION_VALIDATION_QUORUM, strTemp))
VALIDATION_QUORUM = std::max (0, beast::lexicalCastThrow <int> (strTemp));
if (SectionSingleB (secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp))
FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow <std::uint64_t> (strTemp);
if (SectionSingleB (secConfig, SECTION_FEE_OWNER_RESERVE, strTemp))
FEE_OWNER_RESERVE = beast::lexicalCastThrow <std::uint64_t> (strTemp);
if (SectionSingleB (secConfig, SECTION_FEE_NICKNAME_CREATE, strTemp))
FEE_NICKNAME_CREATE = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_FEE_OFFER, strTemp))
FEE_OFFER = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_FEE_DEFAULT, strTemp))
FEE_DEFAULT = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_FEE_OPERATION, strTemp))
FEE_CONTRACT_OPERATION = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_LEDGER_HISTORY, strTemp))
{
boost::to_lower (strTemp);
if (strTemp == "full")
LEDGER_HISTORY = 1000000000u;
else if (strTemp == "none")
LEDGER_HISTORY = 0;
else
LEDGER_HISTORY = beast::lexicalCastThrow <std::uint32_t> (strTemp);
}
if (SectionSingleB (secConfig, SECTION_FETCH_DEPTH, strTemp))
{
boost::to_lower (strTemp);
if (strTemp == "none")
FETCH_DEPTH = 0;
else if (strTemp == "full")
FETCH_DEPTH = 1000000000u;
else
FETCH_DEPTH = beast::lexicalCastThrow <std::uint32_t> (strTemp);
if (FETCH_DEPTH < 10)
FETCH_DEPTH = 10;
}
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_OLD, strTemp))
PATH_SEARCH_OLD = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH, strTemp))
PATH_SEARCH = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_FAST, strTemp))
PATH_SEARCH_FAST = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_MAX, strTemp))
PATH_SEARCH_MAX = beast::lexicalCastThrow <int> (strTemp);
if (SectionSingleB (secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp))
ACCOUNT_PROBE_MAX = beast::lexicalCastThrow <int> (strTemp);
(void) SectionSingleB (secConfig, SECTION_SMS_FROM, SMS_FROM);
(void) SectionSingleB (secConfig, SECTION_SMS_KEY, SMS_KEY);
(void) SectionSingleB (secConfig, SECTION_SMS_SECRET, SMS_SECRET);
(void) SectionSingleB (secConfig, SECTION_SMS_TO, SMS_TO);
(void) SectionSingleB (secConfig, SECTION_SMS_URL, SMS_URL);
if (SectionSingleB (secConfig, SECTION_VALIDATORS_FILE, strTemp))
{
VALIDATORS_FILE = strTemp;
}
if (SectionSingleB (secConfig, SECTION_DEBUG_LOGFILE, strTemp))
DEBUG_LOGFILE = strTemp;
if (SectionSingleB (secConfig, SECTION_CONSOLE_LOG_OUTPUT, strTemp))
CONSOLE_LOG_OUTPUT = strTemp;
}
}
}
int Config::getSize (SizedItemName item)
{
SizedItem sizeTable[] = // tiny small medium large huge
{
{ siSweepInterval, { 10, 30, 60, 90, 120 } },
{ siLedgerFetch, { 2, 2, 3, 3, 3 } },
{ siValidationsSize, { 256, 256, 512, 1024, 1024 } },
{ siValidationsAge, { 500, 500, 500, 500, 500 } },
{ siNodeCacheSize, { 16384, 32768, 131072, 262144, 0 } },
{ siNodeCacheAge, { 60, 90, 120, 900, 0 } },
{ siTreeCacheSize, { 8192, 65536, 131072, 131072, 0 } },
{ siTreeCacheAge, { 30, 60, 90, 120, 900 } },
{ siSLECacheSize, { 4096, 8192, 16384, 65536, 0 } },
{ siSLECacheAge, { 30, 60, 90, 120, 300 } },
{ siLedgerSize, { 32, 128, 256, 384, 0 } },
{ siLedgerAge, { 30, 90, 180, 240, 900 } },
{ siHashNodeDBCache, { 4, 12, 24, 64, 128 } },
{ siTxnDBCache, { 4, 12, 24, 64, 128 } },
{ siLgrDBCache, { 4, 8, 16, 32, 128 } },
};
for (int i = 0; i < (sizeof (sizeTable) / sizeof (SizedItem)); ++i)
{
if (sizeTable[i].item == item)
return sizeTable[i].sizes[NODE_SIZE];
}
assert (false);
return -1;
}
//------------------------------------------------------------------------------
//
// VFALCO NOTE Clean members area
//
Config& getConfig ()
{
static Config config;
return config;
}
//------------------------------------------------------------------------------
beast::File Config::getConfigDir () const
{
beast::String const s (CONFIG_FILE.native().c_str ());
if (s.isNotEmpty ())
return beast::File (s).getParentDirectory ();
return beast::File::nonexistent ();
}
beast::File Config::getDatabaseDir () const
{
beast::String const s (DATA_DIR.native().c_str());
if (s.isNotEmpty ())
return beast::File (s);
return beast::File::nonexistent ();
}
beast::File Config::getValidatorsFile () const
{
beast::String const s (VALIDATORS_FILE.native().c_str());
if (s.isNotEmpty() && getConfigDir() != beast::File::nonexistent())
return getConfigDir().getChildFile (s);
return beast::File::nonexistent ();
}
beast::URL Config::getValidatorsURL () const
{
//String s = "https://" + VALIDATORS_SITE + VALIDATORS_URI;
beast::String s = VALIDATORS_SITE;
return beast::ParsedURL (s).url ();
}
//------------------------------------------------------------------------------
void Config::setRpcIpAndOptionalPort (std::string const& newAddress)
{
beast::String const s (newAddress.c_str ());
int const colonPosition = s.lastIndexOfChar (':');
if (colonPosition != -1)
{
beast::String const ipPart = s.substring (0, colonPosition);
beast::String const portPart = s.substring (colonPosition + 1, s.length ());
setRpcIP (ipPart.toRawUTF8 ());
setRpcPort (portPart.getIntValue ());
}
else
{
setRpcIP (newAddress);
}
}
//------------------------------------------------------------------------------
Config::Role Config::getAdminRole (Json::Value const& params, beast::IP::Endpoint const& remoteIp) const
{
Config::Role role (Config::FORBID);
bool const bPasswordSupplied =
params.isMember ("admin_user") ||
params.isMember ("admin_password");
bool const bPasswordRequired =
! this->RPC_ADMIN_USER.empty () ||
! this->RPC_ADMIN_PASSWORD.empty ();
bool bPasswordWrong;
if (bPasswordSupplied)
{
if (bPasswordRequired)
{
// Required, and supplied, check match
bPasswordWrong =
(this->RPC_ADMIN_USER !=
(params.isMember ("admin_user") ? params["admin_user"].asString () : ""))
||
(this->RPC_ADMIN_PASSWORD !=
(params.isMember ("admin_user") ? params["admin_password"].asString () : ""));
}
else
{
// Not required, but supplied
bPasswordWrong = false;
}
}
else
{
// Required but not supplied,
bPasswordWrong = bPasswordRequired;
}
// Meets IP restriction for admin.
beast::IP::Endpoint const remote_addr (remoteIp.at_port (0));
bool bAdminIP = false;
for (auto const& allow_addr : RPC_ADMIN_ALLOW)
{
if (allow_addr == remote_addr)
{
bAdminIP = true;
break;
}
}
if (bPasswordWrong // Wrong
|| (bPasswordSupplied && !bAdminIP)) // Supplied and doesn't meet IP filter.
{
role = Config::FORBID;
}
// If supplied, password is correct.
else
{
// Allow admin, if from admin IP and no password is required or it was supplied and correct.
role = bAdminIP && (!bPasswordRequired || bPasswordSupplied) ? Config::ADMIN : Config::GUEST;
}
return role;
}
//------------------------------------------------------------------------------
beast::File const& Config::getModuleDatabasePath ()
{
return m_moduleDbPath;
}
//
//
//------------------------------------------------------------------------------
} // ripple

View File

@@ -0,0 +1,498 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CORE_CONFIG_H_INCLUDED
#define RIPPLE_CORE_CONFIG_H_INCLUDED
#include <beast/module/core/files/File.h>
namespace ripple {
// VFALCO TODO Replace these with beast "unsigned long long" generators
// VFALCO NOTE Apparently these are used elsewhere. Make them constants in the config
// or in the Application
//
#define SYSTEM_CURRENCY_GIFT 1000ull
#define SYSTEM_CURRENCY_USERS 100000000ull
#define SYSTEM_CURRENCY_PARTS 1000000ull // 10^SYSTEM_CURRENCY_PRECISION
#define SYSTEM_CURRENCY_START (SYSTEM_CURRENCY_GIFT*SYSTEM_CURRENCY_USERS*SYSTEM_CURRENCY_PARTS)
const int DOMAIN_BYTES_MAX = 256;
const int PUBLIC_BYTES_MAX = 33; // Maximum bytes for an account public key.
const int SYSTEM_PEER_PORT = 6561;
const int SYSTEM_WEBSOCKET_PORT = 6562;
const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
// Allow anonymous DH.
#define DEFAULT_PEER_SSL_CIPHER_LIST "ALL:!LOW:!EXP:!MD5:@STRENGTH"
// Normal, recommend 1 hour: 60*60
// Testing, recommend 1 minute: 60
#define DEFAULT_PEER_SCAN_INTERVAL_MIN (60*60) // Seconds
// Maximum number of peers to try to connect to as client at once.
#define DEFAULT_PEER_START_MAX 5
// Might connect with fewer for testing.
#define DEFAULT_PEER_CONNECT_LOW_WATER 10
#define DEFAULT_PATH_SEARCH_OLD 7
#define DEFAULT_PATH_SEARCH 7
#define DEFAULT_PATH_SEARCH_FAST 2
#define DEFAULT_PATH_SEARCH_MAX 10
enum SizedItemName
{
siSweepInterval,
siValidationsSize,
siValidationsAge,
siNodeCacheSize,
siNodeCacheAge,
siTreeCacheSize,
siTreeCacheAge,
siSLECacheSize,
siSLECacheAge,
siLedgerSize,
siLedgerAge,
siLedgerFetch,
siHashNodeDBCache,
siTxnDBCache,
siLgrDBCache,
};
struct SizedItem
{
SizedItemName item;
int sizes[5];
};
// VFALCO TODO rename all fields to not look like macros, and be more verbose
// VFALCO TODO document every member
class Config
{
public:
//--------------------------------------------------------------------------
//
// VFALCO NOTE To tame this "Config" beast I am breaking it up into
// individual sections related to a specific area of the
// program. For example, listening port configuration. Or
// node database configuration. Each class has its own
// default constructor, and load function for reading in
// settings from the parsed config file data.
//
// Clean member area. Please follow this style for modifying
// or adding code in the file.
struct Helpers
{
// This replaces CONFIG_FILE_NAME
static char const* getConfigFileName ()
{
return "rippled.cfg";
}
static char const* getDatabaseDirName ()
{
return "db";
}
static char const* getValidatorsFileName ()
{
return "validators.txt";
}
};
/** The result of performing a load on the parsed config file data.
This type is convertible to `bool`.
A value of `true` indicates an error occurred,
while `false` indicates no error.
*/
class Error
{
public:
Error () noexcept
: m_what (beast::String::empty)
, m_fileName ("")
, m_lineNumber (0)
{
}
Error (beast::String what, char const* fileName, int lineNumber) noexcept
: m_what (what)
, m_fileName (fileName)
, m_lineNumber (lineNumber)
{
}
explicit
operator bool() const noexcept
{
return m_what != beast::String::empty;
}
beast::String what () const noexcept
{
return m_what;
}
char const* fileName () const
{
return m_fileName;
}
int lineNumber () const
{
return m_lineNumber;
}
private:
beast::String m_what;
char const* m_fileName;
int m_lineNumber;
};
/** Listening socket settings. */
struct DoorSettings
{
/** Create a default set of door (listening socket) settings. */
DoorSettings ();
/** Load settings from the configuration file. */
//Error load (ParsedConfigFile const& file);
};
//--------------------------------------------------------------------------
// Settings related to the configuration file location and directories
/** Returns the directory from which the configuration file was loaded. */
beast::File getConfigDir () const;
/** Returns the directory in which the current database files are located. */
beast::File getDatabaseDir () const;
// LEGACY FIELDS, REMOVE ASAP
boost::filesystem::path CONFIG_FILE; // used by UniqueNodeList
private:
boost::filesystem::path CONFIG_DIR;
public:
// VFALCO TODO Make this private and fix callers to go through getDatabaseDir()
boost::filesystem::path DATA_DIR;
//--------------------------------------------------------------------------
// Settings related to validators
/** Return the path to the separate, optional validators file. */
beast::File getValidatorsFile () const;
/** Returns the optional URL to a trusted network source of validators. */
beast::URL getValidatorsURL () const;
// DEPRECATED
boost::filesystem::path VALIDATORS_FILE; // As specifed in rippled.cfg.
//--------------------------------------------------------------------------
// Settings related to RPC
/** Get the client or server RPC IP address.
@note The string may not always be in a valid parsable state.
@return A string representing the address.
*/
std::string getRpcIP () const { return m_rpcIP; }
/** Get the client or server RPC port number.
@note The port number may be invalid (out of range or zero)
@return The RPC port number.
*/
int getRpcPort () const { return m_rpcPort; }
/** Set the client or server RPC IP and optional port.
@note The string is not syntax checked.
@param newAddress A string in the format <ip-address>[':'<port-number>]
*/
void setRpcIpAndOptionalPort (std::string const& newAddress);
/** Set the client or server RPC IP.
@note The string is not syntax-checked.
@param newIP A string representing the IP address to use.
*/
void setRpcIP (std::string const& newIP) { m_rpcIP = newIP; }
/** Set the client or server RPC port number.
@note The port number is not range checked.
@param newPort The RPC port number to use.
*/
void setRpcPort (int newPort) { m_rpcPort = newPort; }
/** Convert the RPC/port combination to a readable string.
*/
beast::String const getRpcAddress ()
{
beast::String s;
s << m_rpcIP.c_str () << ":" << m_rpcPort;
return s;
}
/** Determine the level of administrative permission to grant.
*/
enum Role
{
GUEST,
USER,
ADMIN,
FORBID
};
Role getAdminRole (Json::Value const& params, beast::IP::Endpoint const& remoteIp) const;
/** Listening port number for peer connections. */
int peerListeningPort;
/** PROXY listening port number
If this is not zero, it indicates an additional port number on
which we should accept incoming Peer connections that will also
require a PROXY handshake.
The PROXY Protocol:
http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
*/
int peerPROXYListeningPort;
/** List of Validators entries from rippled.cfg */
std::vector <std::string> validators;
private:
std::string m_rpcIP;
int m_rpcPort; // VFALCO TODO This should be a short.
private:
/** The folder where new module databases should be located */
beast::File m_moduleDbPath;
public:
//--------------------------------------------------------------------------
/** Returns the location were databases should be located
The location may be a file, in which case databases should be placed in
the file, or it may be a directory, in which cases databases should be
stored in a file named after the module (e.g. "peerfinder.sqlite") that
is inside that directory.
*/
beast::File const& getModuleDatabasePath ();
//--------------------------------------------------------------------------
/** Parameters for the insight collection module */
beast::StringPairArray insightSettings;
/** Parameters for the main NodeStore database.
This is 1 or more strings of the form <key>=<value>
The 'type' and 'path' keys are required, see rippled-example.cfg
@see Database
*/
beast::StringPairArray nodeDatabase;
/** Parameters for the ephemeral NodeStore database.
This is an auxiliary database for the NodeStore, usually placed
on a separate faster volume. However, the volume data may not persist
between launches. Use of the ephemeral database is optional.
The format is the same as that for @ref nodeDatabase
@see Database
*/
beast::StringPairArray ephemeralNodeDatabase;
/** Parameters for importing an old database in to the current node database.
If this is not empty, then it specifies the key/value parameters for
another node database from which to import all data into the current
node database specified by @ref nodeDatabase.
The format of this string is in the form:
<key>'='<value>['|'<key>'='value]
@see parseDelimitedKeyValueString
*/
bool doImport;
beast::StringPairArray importNodeDatabase;
//
//
//--------------------------------------------------------------------------
public:
// Configuration parameters
bool QUIET;
boost::filesystem::path DEBUG_LOGFILE;
std::string CONSOLE_LOG_OUTPUT;
bool ELB_SUPPORT; // Support Amazon ELB
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.
std::string VALIDATORS_URI; // URI of validators.txt.
std::string VALIDATORS_BASE; // Name
std::vector<std::string> IPS; // Peer IPs from rippled.cfg.
std::vector<std::string> IPS_FIXED; // Fixed Peer IPs from rippled.cfg.
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
enum StartUpType
{
FRESH,
NORMAL,
LOAD,
LOAD_FILE,
REPLAY,
NETWORK
};
StartUpType START_UP;
std::string START_LEDGER;
// Database
std::string DATABASE_PATH;
// Network parameters
int NETWORK_START_TIME; // The Unix time we start ledger 0.
int TRANSACTION_FEE_BASE; // The number of fee units a reference transaction costs
int LEDGER_SECONDS;
int LEDGER_PROPOSAL_DELAY_SECONDS;
int LEDGER_AVALANCHE_SECONDS;
bool LEDGER_CREATOR; // Should be false unless we are starting a new ledger.
/** Operate in stand-alone mode.
In stand alone mode:
- Peer connections are not attempted or accepted
- The ledger is not advanced automatically.
- If no ledger is loaded, the default ledger with the root
account is created.
*/
bool RUN_STANDALONE;
// Note: The following parameters do not relate to the UNL or trust at all
unsigned int NETWORK_QUORUM; // Minimum number of nodes to consider the network present
int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
// Peer networking parameters
std::string PEER_IP;
int NUMBER_CONNECTIONS;
std::string PEER_SSL_CIPHER_LIST;
int PEER_SCAN_INTERVAL_MIN;
int PEER_START_MAX;
unsigned int PEER_CONNECT_LOW_WATER;
bool PEER_PRIVATE; // True to ask peers not to relay current IP.
unsigned int PEERS_MAX;
// Websocket networking parameters
std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction.
int WEBSOCKET_PUBLIC_PORT;
int WEBSOCKET_PUBLIC_SECURE;
std::string WEBSOCKET_PROXY_IP; // XXX Going away. Merge with the inbound peer connction.
int WEBSOCKET_PROXY_PORT;
int WEBSOCKET_PROXY_SECURE;
std::string WEBSOCKET_IP;
int WEBSOCKET_PORT;
int WEBSOCKET_SECURE;
int WEBSOCKET_PING_FREQ;
std::string WEBSOCKET_SSL_CERT;
std::string WEBSOCKET_SSL_CHAIN;
std::string WEBSOCKET_SSL_KEY;
// RPC parameters
std::vector<beast::IP::Endpoint> RPC_ADMIN_ALLOW;
std::string RPC_ADMIN_PASSWORD;
std::string RPC_ADMIN_USER;
std::string RPC_PASSWORD;
std::string RPC_USER;
bool RPC_ALLOW_REMOTE;
Json::Value RPC_STARTUP;
int RPC_SECURE;
std::string RPC_SSL_CERT;
std::string RPC_SSL_CHAIN;
std::string RPC_SSL_KEY;
// Path searching
int PATH_SEARCH_OLD;
int PATH_SEARCH;
int PATH_SEARCH_FAST;
int PATH_SEARCH_MAX;
// Validation
RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV;
// Node/Cluster
std::vector<std::string> CLUSTER_NODES;
RippleAddress NODE_SEED, NODE_PUB, NODE_PRIV;
// Fee schedule (All below values are in fee units)
std::uint64_t FEE_DEFAULT; // Default fee.
std::uint64_t FEE_ACCOUNT_RESERVE; // Amount of units not allowed to send.
std::uint64_t FEE_OWNER_RESERVE; // Amount of units not allowed to send per owner entry.
std::uint64_t FEE_NICKNAME_CREATE; // Fee to create a nickname.
std::uint64_t FEE_OFFER; // Rate per day.
int FEE_CONTRACT_OPERATION; // fee for each contract operation
// Node storage configuration
std::uint32_t LEDGER_HISTORY;
std::uint32_t FETCH_DEPTH;
int NODE_SIZE;
// Client behavior
int ACCOUNT_PROBE_MAX; // How far to scan for accounts.
// Signing signatures.
std::uint32_t SIGN_TRANSACTION;
std::uint32_t SIGN_VALIDATION;
std::uint32_t SIGN_PROPOSAL;
bool SSL_VERIFY;
std::string SSL_VERIFY_FILE;
std::string SSL_VERIFY_DIR;
std::string SMS_FROM;
std::string SMS_KEY;
std::string SMS_SECRET;
std::string SMS_TO;
std::string SMS_URL;
public:
Config ();
int getSize (SizedItemName);
void setup (const std::string& strConf, bool bQuiet);
void load ();
};
extern Config& getConfig ();
} // ripple
#endif

View File

@@ -0,0 +1,115 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONFIGSECTIONS_H_INCLUDED
#define RIPPLE_CONFIGSECTIONS_H_INCLUDED
namespace ripple {
// VFALCO NOTE
//
// Please use this style for all new sections
// And if you're feeling generous, convert all the
// existing macros to this format as well.
//
struct ConfigSection
{
static beast::String nodeDatabase () { return "node_db"; }
static beast::String tempNodeDatabase () { return "temp_db"; }
static beast::String importNodeDatabase () { return "import_db"; }
};
// VFALCO TODO Rename and replace these macros with variables.
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
#define SECTION_CLUSTER_NODES "cluster_nodes"
#define SECTION_DATABASE_PATH "database_path"
#define SECTION_DEBUG_LOGFILE "debug_logfile"
#define SECTION_CONSOLE_LOG_OUTPUT "console_log_output"
#define SECTION_ELB_SUPPORT "elb_support"
#define SECTION_FEE_DEFAULT "fee_default"
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
#define SECTION_FEE_OFFER "fee_offer"
#define SECTION_FEE_OPERATION "fee_operation"
#define SECTION_FEE_ACCOUNT_RESERVE "fee_account_reserve"
#define SECTION_FEE_OWNER_RESERVE "fee_owner_reserve"
#define SECTION_FETCH_DEPTH "fetch_depth"
#define SECTION_LEDGER_HISTORY "ledger_history"
#define SECTION_INSIGHT "insight"
#define SECTION_IPS "ips"
#define SECTION_IPS_FIXED "ips_fixed"
#define SECTION_NETWORK_QUORUM "network_quorum"
#define SECTION_NODE_SEED "node_seed"
#define SECTION_NODE_SIZE "node_size"
#define SECTION_PATH_SEARCH_OLD "path_search_old"
#define SECTION_PATH_SEARCH "path_search"
#define SECTION_PATH_SEARCH_FAST "path_search_fast"
#define SECTION_PATH_SEARCH_MAX "path_search_max"
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
#define SECTION_PEER_IP "peer_ip"
#define SECTION_PEER_PORT "peer_port"
#define SECTION_PEER_PROXY_PORT "peer_port_proxy"
#define SECTION_PEER_PRIVATE "peer_private"
#define SECTION_PEERS_MAX "peers_max"
#define SECTION_PEER_SCAN_INTERVAL_MIN "peer_scan_interval_min"
#define SECTION_PEER_SSL_CIPHER_LIST "peer_ssl_cipher_list"
#define SECTION_PEER_START_MAX "peer_start_max"
#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote"
#define SECTION_RPC_ADMIN_ALLOW "rpc_admin_allow"
#define SECTION_RPC_ADMIN_USER "rpc_admin_user"
#define SECTION_RPC_ADMIN_PASSWORD "rpc_admin_password"
#define SECTION_RPC_IP "rpc_ip"
#define SECTION_RPC_PORT "rpc_port"
#define SECTION_RPC_USER "rpc_user"
#define SECTION_RPC_PASSWORD "rpc_password"
#define SECTION_RPC_STARTUP "rpc_startup"
#define SECTION_RPC_SECURE "rpc_secure"
#define SECTION_RPC_SSL_CERT "rpc_ssl_cert"
#define SECTION_RPC_SSL_CHAIN "rpc_ssl_chain"
#define SECTION_RPC_SSL_KEY "rpc_ssl_key"
#define SECTION_SMS_FROM "sms_from"
#define SECTION_SMS_KEY "sms_key"
#define SECTION_SMS_SECRET "sms_secret"
#define SECTION_SMS_TO "sms_to"
#define SECTION_SMS_URL "sms_url"
#define SECTION_SNTP "sntp_servers"
#define SECTION_SSL_VERIFY "ssl_verify"
#define SECTION_SSL_VERIFY_FILE "ssl_verify_file"
#define SECTION_SSL_VERIFY_DIR "ssl_verify_dir"
#define SECTION_VALIDATORS_FILE "validators_file"
#define SECTION_VALIDATION_QUORUM "validation_quorum"
#define SECTION_VALIDATION_SEED "validation_seed"
#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip"
#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port"
#define SECTION_WEBSOCKET_PUBLIC_SECURE "websocket_public_secure"
#define SECTION_WEBSOCKET_PROXY_IP "websocket_proxy_ip"
#define SECTION_WEBSOCKET_PROXY_PORT "websocket_proxy_port"
#define SECTION_WEBSOCKET_PROXY_SECURE "websocket_proxy_secure"
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
#define SECTION_WEBSOCKET_IP "websocket_ip"
#define SECTION_WEBSOCKET_PORT "websocket_port"
#define SECTION_WEBSOCKET_SECURE "websocket_secure"
#define SECTION_WEBSOCKET_SSL_CERT "websocket_ssl_cert"
#define SECTION_WEBSOCKET_SSL_CHAIN "websocket_ssl_chain"
#define SECTION_WEBSOCKET_SSL_KEY "websocket_ssl_key"
#define SECTION_VALIDATORS "validators"
#define SECTION_VALIDATORS_SITE "validators_site"
} // ripple
#endif

View File

@@ -0,0 +1,130 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace ripple {
Job::Job ()
: mType (jtINVALID)
, mJobIndex (0)
{
}
Job::Job (JobType type, std::uint64_t index)
: mType (type)
, mJobIndex (index)
{
}
Job::Job (JobType type,
std::string const& name,
std::uint64_t index,
LoadMonitor& lm,
std::function <void (Job&)> const& job,
CancelCallback cancelCallback)
: m_cancelCallback (cancelCallback)
, mType (type)
, mJobIndex (index)
, mJob (job)
, mName (name)
, m_queue_time (clock_type::now ())
{
m_loadEvent = boost::make_shared <LoadEvent> (boost::ref (lm), name, false);
}
JobType Job::getType () const
{
return mType;
}
CancelCallback Job::getCancelCallback () const
{
bassert (m_cancelCallback);
return m_cancelCallback;
}
Job::clock_type::time_point const& Job::queue_time () const
{
return m_queue_time;
}
bool Job::shouldCancel () const
{
if (m_cancelCallback)
return m_cancelCallback ();
return false;
}
void Job::doJob ()
{
m_loadEvent->start ();
m_loadEvent->reName (mName);
mJob (*this);
}
void Job::rename (std::string const& newName)
{
mName = newName;
}
bool Job::operator> (const Job& j) const
{
if (mType < j.mType)
return true;
if (mType > j.mType)
return false;
return mJobIndex > j.mJobIndex;
}
bool Job::operator>= (const Job& j) const
{
if (mType < j.mType)
return true;
if (mType > j.mType)
return false;
return mJobIndex >= j.mJobIndex;
}
bool Job::operator< (const Job& j) const
{
if (mType < j.mType)
return false;
if (mType > j.mType)
return true;
return mJobIndex < j.mJobIndex;
}
bool Job::operator<= (const Job& j) const
{
if (mType < j.mType)
return false;
if (mType > j.mType)
return true;
return mJobIndex <= j.mJobIndex;
}
}

View File

@@ -0,0 +1,143 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_JOB_H
#define RIPPLE_JOB_H
namespace ripple {
// Note that this queue should only be used for CPU-bound jobs
// It is primarily intended for signature checking
enum JobType
{
// Special type indicating an invalid job - will go away soon.
jtINVALID = -1,
// Job types - the position in this enum indicates the job priority with
// earlier jobs having lower priority than later jobs. If you wish to
// insert a job at a specific priority, simply add it at the right location.
jtPACK, // Make a fetch pack for a peer
jtPUBOLDLEDGER, // An old ledger has been accepted
jtVALIDATION_ut, // A validation from an untrusted source
jtPROOFWORK, // A proof of work demand from another server
jtTRANSACTION_l, // A local transaction
jtPROPOSAL_ut, // A proposal from an untrusted source
jtLEDGER_DATA, // Received data for a ledger we're acquiring
jtCLIENT, // A websocket command from the client
jtRPC, // A websocket command from the client
jtUPDATE_PF, // Update pathfinding requests
jtTRANSACTION, // A transaction received from the network
jtUNL, // A Score or Fetch of the UNL (DEPRECATED)
jtADVANCE, // Advance validated/acquired ledgers
jtPUBLEDGER, // Publish a fully-accepted ledger
jtTXN_DATA, // Fetch a proposed set
jtWAL, // Write-ahead logging
jtVALIDATION_t, // A validation from a trusted source
jtWRITE, // Write out hashed objects
jtACCEPT, // Accept a consensus ledger
jtPROPOSAL_t, // A proposal from a trusted source
jtSWEEP, // Sweep for stale structures
jtNETOP_CLUSTER, // NetworkOPs cluster peer report
jtNETOP_TIMER, // NetworkOPs net timer processing
jtADMIN, // An administrative operation
// Special job types which are not dispatched by the job pool
jtPEER ,
jtDISK ,
jtTXN_PROC ,
jtOB_SETUP ,
jtPATH_FIND ,
jtHO_READ ,
jtHO_WRITE ,
jtGENERIC , // Used just to measure time
// Node store monitoring
jtNS_SYNC_READ ,
jtNS_ASYNC_READ ,
jtNS_WRITE ,
};
class Job
{
public:
typedef std::chrono::steady_clock clock_type;
/** Default constructor.
Allows Job to be used as a container type.
This is used to allow things like jobMap [key] = value.
*/
// VFALCO NOTE I'd prefer not to have a default constructed object.
// What is the semantic meaning of a Job with no associated
// function? Having the invariant "all Job objects refer to
// a job" would reduce the number of states.
//
Job ();
//Job (Job const& other);
Job (JobType type, std::uint64_t index);
// VFALCO TODO try to remove the dependency on LoadMonitor.
Job (JobType type,
std::string const& name,
std::uint64_t index,
LoadMonitor& lm,
std::function <void (Job&)> const& job,
CancelCallback cancelCallback);
//Job& operator= (Job const& other);
JobType getType () const;
CancelCallback getCancelCallback () const;
/** Returns the time when the job was queued. */
clock_type::time_point const& queue_time () const;
/** Returns `true` if the running job should make a best-effort cancel. */
bool shouldCancel () const;
void doJob ();
void rename (const std::string& n);
// These comparison operators make the jobs sort in priority order
// in the job set
bool operator< (const Job& j) const;
bool operator> (const Job& j) const;
bool operator<= (const Job& j) const;
bool operator>= (const Job& j) const;
private:
CancelCallback m_cancelCallback;
JobType mType;
std::uint64_t mJobIndex;
std::function <void (Job&)> mJob;
LoadEvent::pointer m_loadEvent;
std::string mName;
clock_type::time_point m_queue_time;
};
}
#endif

View File

@@ -0,0 +1,691 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/module/core/functional/JobQueue.h>
#include <ripple/module/core/functional/JobTypes.h>
#include <ripple/module/core/functional/JobTypeInfo.h>
#include <ripple/module/core/functional/JobTypeData.h>
#include <beast/cxx14/memory.h>
#include <beast/chrono/chrono_util.h>
#include <beast/module/core/thread/Workers.h>
#include <beast/module/core/system/SystemStats.h>
#include <chrono>
namespace ripple {
class JobQueueImp
: public JobQueue
, private beast::Workers::Callback
{
public:
typedef std::set <Job> JobSet;
typedef std::map <JobType, JobTypeData> JobDataMap;
typedef beast::CriticalSection::ScopedLockType ScopedLock;
beast::Journal m_journal;
beast::CriticalSection m_mutex;
std::uint64_t m_lastJob;
JobSet m_jobSet;
JobDataMap m_jobData;
JobTypeData m_invalidJobData;
// The number of jobs currently in processTask()
int m_processCount;
beast::Workers m_workers;
CancelCallback m_cancelCallback;
// statistics tracking
beast::insight::Collector::ptr m_collector;
beast::insight::Gauge job_count;
beast::insight::Hook hook;
//--------------------------------------------------------------------------
static JobTypes const& getJobTypes ()
{
static JobTypes types;
return types;
}
//--------------------------------------------------------------------------
JobQueueImp (beast::insight::Collector::ptr const& collector,
Stoppable& parent, beast::Journal journal)
: JobQueue ("JobQueue", parent)
, m_journal (journal)
, m_lastJob (0)
, m_invalidJobData (getJobTypes ().getInvalid (), collector)
, m_processCount (0)
, m_workers (*this, "JobQueue", 0)
, m_cancelCallback (boost::bind (&Stoppable::isStopping, this))
, m_collector (collector)
{
hook = m_collector->make_hook (std::bind (
&JobQueueImp::collect, this));
job_count = m_collector->make_gauge ("job_count");
{
ScopedLock lock (m_mutex);
for (auto const& x : getJobTypes ())
{
JobTypeInfo const& jt = x.second;
// And create dynamic information for all jobs
auto const result (m_jobData.emplace (std::piecewise_construct,
std::forward_as_tuple (jt.type ()),
std::forward_as_tuple (jt, m_collector)));
assert (result.second == true);
}
}
}
~JobQueueImp ()
{
// Must unhook before destroying
hook = beast::insight::Hook ();
}
void collect ()
{
ScopedLock lock (m_mutex);
job_count = m_jobSet.size ();
}
void addJob (JobType type, std::string const& name,
boost::function <void (Job&)> const& jobFunc)
{
assert (type != jtINVALID);
JobDataMap::iterator iter (m_jobData.find (type));
assert (iter != m_jobData.end ());
if (iter == m_jobData.end ())
return;
JobTypeData& data (iter->second);
// FIXME: Workaround incorrect client shutdown ordering
// do not add jobs to a queue with no threads
assert (type == jtCLIENT || m_workers.getNumberOfThreads () > 0);
{
// If this goes off it means that a child didn't follow
// the Stoppable API rules. A job may only be added if:
//
// - The JobQueue has NOT stopped
// AND
// * We are currently processing jobs
// OR
// * We have have pending jobs
// OR
// * Not all children are stopped
//
ScopedLock lock (m_mutex);
assert (! isStopped() && (
m_processCount>0 ||
! m_jobSet.empty () ||
! areChildrenStopped()));
}
// Don't even add it to the queue if we're stopping
// and the job type is marked for skipOnStop.
//
if (isStopping() && skipOnStop (type))
{
m_journal.debug <<
"Skipping addJob ('" << name << "')";
return;
}
{
ScopedLock lock (m_mutex);
std::pair <std::set <Job>::iterator, bool> result (
m_jobSet.insert (Job (type, name, ++m_lastJob,
data.load (), jobFunc, m_cancelCallback)));
queueJob (*result.first, lock);
}
}
int getJobCount (JobType t)
{
ScopedLock lock (m_mutex);
JobDataMap::const_iterator c = m_jobData.find (t);
return (c == m_jobData.end ())
? 0
: c->second.waiting;
}
int getJobCountTotal (JobType t)
{
ScopedLock lock (m_mutex);
JobDataMap::const_iterator c = m_jobData.find (t);
return (c == m_jobData.end ())
? 0
: (c->second.waiting + c->second.running);
}
int getJobCountGE (JobType t)
{
// return the number of jobs at this priority level or greater
int ret = 0;
ScopedLock lock (m_mutex);
for (auto const& x : m_jobData)
{
if (x.first >= t)
ret += x.second.waiting;
}
return ret;
}
// shut down the job queue without completing pending jobs
//
void shutdown ()
{
m_journal.info << "Job queue shutting down";
m_workers.pauseAllThreadsAndWait ();
}
// set the number of thread serving the job queue to precisely this number
void setThreadCount (int c, bool const standaloneMode)
{
if (standaloneMode)
{
c = 1;
}
else if (c == 0)
{
c = beast::SystemStats::getNumCpus ();
// VFALCO NOTE According to boost, hardware_concurrency cannot return
// negative numbers/
//
if (c < 0)
c = 2; // VFALCO NOTE Why 2?
if (c > 4) // I/O will bottleneck
c = 4;
c += 2;
m_journal.info << "Auto-tuning to " << c <<
" validation/transaction/proposal threads";
}
m_workers.setNumberOfThreads (c);
}
LoadEvent::pointer getLoadEvent (JobType t, const std::string& name)
{
JobDataMap::iterator iter (m_jobData.find (t));
assert (iter != m_jobData.end ());
if (iter == m_jobData.end ())
return boost::shared_ptr<LoadEvent> ();
return boost::make_shared<LoadEvent> (
boost::ref (iter-> second.load ()), name, true);
}
LoadEvent::autoptr getLoadEventAP (JobType t, const std::string& name)
{
JobDataMap::iterator iter (m_jobData.find (t));
assert (iter != m_jobData.end ());
if (iter == m_jobData.end ())
return LoadEvent::autoptr ();
return LoadEvent::autoptr (
new LoadEvent (iter-> second.load (), name, true));
}
void addLoadEvents (JobType t,
int count, std::chrono::milliseconds elapsed)
{
JobDataMap::iterator iter (m_jobData.find (t));
assert (iter != m_jobData.end ());
iter->second.load().addSamples (count, elapsed);
}
bool isOverloaded ()
{
int count = 0;
for (auto& x : m_jobData)
{
if (x.second.load ().isOver ())
++count;
}
return count > 0;
}
Json::Value getJson (int)
{
Json::Value ret (Json::objectValue);
ret["threads"] = m_workers.getNumberOfThreads ();
Json::Value priorities = Json::arrayValue;
ScopedLock lock (m_mutex);
for (auto& x : m_jobData)
{
assert (x.first != jtINVALID);
if (x.first == jtGENERIC)
continue;
JobTypeData& data (x.second);
LoadMonitor::Stats stats (data.stats ());
int waiting (data.waiting);
int running (data.running);
if ((stats.count != 0) || (waiting != 0) ||
(stats.latencyPeak != 0) || (running != 0))
{
Json::Value& pri = priorities.append (Json::objectValue);
pri["job_type"] = data.name ();
if (stats.isOverloaded)
pri["over_target"] = true;
if (waiting != 0)
pri["waiting"] = waiting;
if (stats.count != 0)
pri["per_second"] = static_cast<int> (stats.count);
if (stats.latencyPeak != 0)
pri["peak_time"] = static_cast<int> (stats.latencyPeak);
if (stats.latencyAvg != 0)
pri["avg_time"] = static_cast<int> (stats.latencyAvg);
if (running != 0)
pri["in_progress"] = running;
}
}
ret["job_types"] = priorities;
return ret;
}
private:
//--------------------------------------------------------------------------
JobTypeData& getJobTypeData (JobType type)
{
JobDataMap::iterator c (m_jobData.find (type));
assert (c != m_jobData.end ());
// NIKB: This is ugly and I hate it. We must remove jtINVALID completely
// and use something sane.
if (c == m_jobData.end ())
return m_invalidJobData;
return c->second;
}
//--------------------------------------------------------------------------
// Signals the service stopped if the stopped condition is met.
//
void checkStopped (ScopedLock const& lock)
{
// We are stopped when all of the following are true:
//
// 1. A stop notification was received
// 2. All Stoppable children have stopped
// 3. There are no executing calls to processTask
// 4. There are no remaining Jobs in the job set
//
if (isStopping() &&
areChildrenStopped() &&
(m_processCount == 0) &&
m_jobSet.empty())
{
stopped();
}
}
//--------------------------------------------------------------------------
//
// Signals an added Job for processing.
//
// Pre-conditions:
// The JobType must be valid.
// The Job must exist in mJobSet.
// The Job must not have previously been queued.
//
// Post-conditions:
// Count of waiting jobs of that type will be incremented.
// If JobQueue exists, and has at least one thread, Job will eventually run.
//
// Invariants:
// The calling thread owns the JobLock
//
void queueJob (Job const& job, ScopedLock const& lock)
{
JobType const type (job.getType ());
assert (type != jtINVALID);
assert (m_jobSet.find (job) != m_jobSet.end ());
JobTypeData& data (getJobTypeData (type));
if (data.waiting + data.running < getJobLimit (type))
{
m_workers.addTask ();
}
else
{
// defer the task until we go below the limit
//
++data.deferred;
}
++data.waiting;
}
//------------------------------------------------------------------------------
//
// Returns the next Job we should run now.
//
// RunnableJob:
// A Job in the JobSet whose slots count for its type is greater than zero.
//
// Pre-conditions:
// mJobSet must not be empty.
// mJobSet holds at least one RunnableJob
//
// Post-conditions:
// job is a valid Job object.
// job is removed from mJobQueue.
// Waiting job count of it's type is decremented
// Running job count of it's type is incremented
//
// Invariants:
// The calling thread owns the JobLock
//
void getNextJob (Job& job, ScopedLock const& lock)
{
assert (! m_jobSet.empty ());
JobSet::const_iterator iter;
for (iter = m_jobSet.begin (); iter != m_jobSet.end (); ++iter)
{
JobTypeData& data (getJobTypeData (iter->getType ()));
assert (data.running <= getJobLimit (data.type ()));
// Run this job if we're running below the limit.
if (data.running < getJobLimit (data.type ()))
{
assert (data.waiting > 0);
break;
}
}
assert (iter != m_jobSet.end ());
JobType const type = iter->getType ();
JobTypeData& data (getJobTypeData (type));
assert (type != jtINVALID);
job = *iter;
m_jobSet.erase (iter);
--data.waiting;
++data.running;
}
//------------------------------------------------------------------------------
//
// Indicates that a running Job has completed its task.
//
// Pre-conditions:
// Job must not exist in mJobSet.
// The JobType must not be invalid.
//
// Post-conditions:
// The running count of that JobType is decremented
// A new task is signaled if there are more waiting Jobs than the limit, if any.
//
// Invariants:
// <none>
//
void finishJob (Job const& job, ScopedLock const& lock)
{
JobType const type = job.getType ();
assert (m_jobSet.find (job) == m_jobSet.end ());
assert (type != jtINVALID);
JobTypeData& data (getJobTypeData (type));
// Queue a deferred task if possible
if (data.deferred > 0)
{
assert (data.running + data.waiting >= getJobLimit (type));
--data.deferred;
m_workers.addTask ();
}
--data.running;
}
//--------------------------------------------------------------------------
template <class Rep, class Period>
void on_dequeue (JobType type,
std::chrono::duration <Rep, Period> const& value)
{
auto const ms (ceil <std::chrono::milliseconds> (value));
if (ms.count() >= 10)
getJobTypeData (type).dequeue.notify (ms);
}
template <class Rep, class Period>
void on_execute (JobType type,
std::chrono::duration <Rep, Period> const& value)
{
auto const ms (ceil <std::chrono::milliseconds> (value));
if (ms.count() >= 10)
getJobTypeData (type).execute.notify (ms);
}
//--------------------------------------------------------------------------
//
// Runs the next appropriate waiting Job.
//
// Pre-conditions:
// A RunnableJob must exist in the JobSet
//
// Post-conditions:
// The chosen RunnableJob will have Job::doJob() called.
//
// Invariants:
// <none>
//
void processTask ()
{
Job job;
{
ScopedLock lock (m_mutex);
getNextJob (job, lock);
++m_processCount;
}
JobTypeData& data (getJobTypeData (job.getType ()));
// Skip the job if we are stopping and the
// skipOnStop flag is set for the job type
//
if (!isStopping() || !data.info.skip ())
{
beast::Thread::setCurrentThreadName (data.name ());
m_journal.trace << "Doing " << data.name () << " job";
Job::clock_type::time_point const start_time (
Job::clock_type::now());
on_dequeue (job.getType (), start_time - job.queue_time ());
job.doJob ();
on_execute (job.getType (), Job::clock_type::now() - start_time);
}
else
{
m_journal.trace << "Skipping processTask ('" << data.name () << "')";
}
{
ScopedLock lock (m_mutex);
finishJob (job, lock);
--m_processCount;
checkStopped (lock);
}
// Note that when Job::~Job is called, the last reference
// to the associated LoadEvent object (in the Job) may be destroyed.
}
//------------------------------------------------------------------------------
// Returns `true` if all jobs of this type should be skipped when
// the JobQueue receives a stop notification. If the job type isn't
// skipped, the Job will be called and the job must call Job::shouldCancel
// to determine if a long running or non-mandatory operation should be canceled.
bool skipOnStop (JobType type)
{
JobTypeInfo const& j (getJobTypes ().get (type));
assert (j.type () != jtINVALID);
return j.skip ();
}
// Returns the limit of running jobs for the given job type.
// For jobs with no limit, we return the largest int. Hopefully that
// will be enough.
//
int getJobLimit (JobType type)
{
JobTypeInfo const& j (getJobTypes ().get (type));
assert (j.type () != jtINVALID);
return j.limit ();
}
//--------------------------------------------------------------------------
void onStop ()
{
// VFALCO NOTE I wanted to remove all the jobs that are skippable
// but then the Workers count of tasks to process
// goes wrong.
/*
{
ScopedLock lock (m_mutex);
// Remove all jobs whose type is skipOnStop
typedef ripple::unordered_map <JobType, std::size_t> JobDataMap;
JobDataMap counts;
bool const report (m_journal.debug.active());
for (JobSet::const_iterator iter (m_jobSet.begin());
iter != m_jobSet.end();)
{
if (skipOnStop (iter->getType()))
{
if (report)
{
std::pair <JobDataMap::iterator, bool> result (
counts.insert (std::make_pair (iter->getType(), 1)));
if (! result.second)
++(result.first->second);
}
iter = m_jobSet.erase (iter);
}
else
{
++iter;
}
}
if (report)
{
beast::Journal::ScopedStream s (m_journal.debug);
for (JobDataMap::const_iterator iter (counts.begin());
iter != counts.end(); ++iter)
{
s << std::endl <<
"Removed " << iter->second <<
" skiponStop jobs of type " << Job::toString (iter->first);
}
}
}
*/
}
void onChildrenStopped ()
{
ScopedLock lock (m_mutex);
checkStopped (lock);
}
};
//------------------------------------------------------------------------------
JobQueue::JobQueue (char const* name, Stoppable& parent)
: Stoppable (name, parent)
{
}
//------------------------------------------------------------------------------
std::unique_ptr <JobQueue> make_JobQueue (
beast::insight::Collector::ptr const& collector,
beast::Stoppable& parent, beast::Journal journal)
{
return std::make_unique <JobQueueImp> (collector, parent, journal);
}
}

View File

@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CORE_JOBQUEUE_H_INCLUDED
#define RIPPLE_CORE_JOBQUEUE_H_INCLUDED
#include <beast/threads/Stoppable.h>
namespace ripple {
class JobQueue : public beast::Stoppable
{
protected:
JobQueue (char const* name, Stoppable& parent);
public:
virtual ~JobQueue () { }
// VFALCO NOTE Using boost::function here because Visual Studio 2012
// std::function doesn't swallow return types.
//
// TODO Replace with std::function
//
virtual void addJob (JobType type,
std::string const& name, boost::function <void (Job&)> const& job) = 0;
// Jobs waiting at this priority
virtual int getJobCount (JobType t) = 0;
// Jobs waiting plus running at this priority
virtual int getJobCountTotal (JobType t) = 0;
// All waiting jobs at or greater than this priority
virtual int getJobCountGE (JobType t) = 0;
virtual void shutdown () = 0;
virtual void setThreadCount (int c, bool const standaloneMode) = 0;
// VFALCO TODO Rename these to newLoadEventMeasurement or something similar
// since they create the object.
//
virtual LoadEvent::pointer getLoadEvent (JobType t, const std::string& name) = 0;
// VFALCO TODO Why do we need two versions, one which returns a shared
// pointer and the other which returns an autoptr?
//
virtual LoadEvent::autoptr getLoadEventAP (JobType t, const std::string& name) = 0;
// Add multiple load events
virtual void addLoadEvents (JobType t,
int count, std::chrono::milliseconds elapsed) = 0;
virtual bool isOverloaded () = 0;
virtual Json::Value getJson (int c = 0) = 0;
};
std::unique_ptr <JobQueue> make_JobQueue (beast::insight::Collector::ptr const& collector,
beast::Stoppable& parent, beast::Journal journal);
}
#endif

View File

@@ -0,0 +1,99 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CORE_JOBTYPEDATA_H_INCLUDED
#define RIPPLE_CORE_JOBTYPEDATA_H_INCLUDED
#include <ripple/module/core/functional/JobTypeInfo.h>
namespace ripple
{
struct JobTypeData
{
private:
LoadMonitor m_load;
/* Support for insight */
beast::insight::Collector::ptr m_collector;
public:
/* The job category which we represent */
JobTypeInfo const& info;
/* The number of jobs waiting */
int waiting;
/* The number presently running */
int running;
/* And the number we deferred executing because of job limits */
int deferred;
/* Notification callbacks */
beast::insight::Event dequeue;
beast::insight::Event execute;
explicit JobTypeData (JobTypeInfo const& info_,
beast::insight::Collector::ptr const& collector) noexcept
: m_collector (collector)
, info (info_)
, waiting (0)
, running (0)
, deferred (0)
{
m_load.setTargetLatency (
info.getAverageLatency (),
info.getPeakLatency());
if (!info.special ())
{
dequeue = m_collector->make_event (info.name () + "_q");
execute = m_collector->make_event (info.name ());
}
}
/* Not copy-constructible or assignable */
JobTypeData (JobTypeData const& other) = delete;
JobTypeData& operator= (JobTypeData const& other) = delete;
std::string name () const
{
return info.name ();
}
JobType type () const
{
return info.type ();
}
LoadMonitor& load ()
{
return m_load;
}
LoadMonitor::Stats stats ()
{
return m_load.getStats ();
}
};
}
#endif

View File

@@ -0,0 +1,101 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CORE_JOBTYPEINFO_H_INCLUDED
#define RIPPLE_CORE_JOBTYPEINFO_H_INCLUDED
namespace ripple
{
/** Holds all the 'static' information about a job, which does not change */
class JobTypeInfo
{
private:
JobType const m_type;
std::string const m_name;
/** The limit on the number of running jobs for this job type. */
int const m_limit;
/** Can be skipped */
bool const m_skip;
/** Special jobs are not dispatched via the job queue */
bool const m_special;
/** Average and peak latencies for this job type. 0 is none specified */
std::uint64_t const m_avgLatency;
std::uint64_t const m_peakLatency;
public:
// Not default constructible
JobTypeInfo () = delete;
JobTypeInfo (JobType type, std::string name, int limit,
bool skip, bool special, std::uint64_t avgLatency, std::uint64_t peakLatency)
: m_type (type)
, m_name (name)
, m_limit (limit)
, m_skip (skip)
, m_special (special)
, m_avgLatency (avgLatency)
, m_peakLatency (peakLatency)
{
}
JobType type () const
{
return m_type;
}
std::string name () const
{
return m_name;
}
int limit () const
{
return m_limit;
}
bool skip () const
{
return m_skip;
}
bool special () const
{
return m_special;
}
std::uint64_t getAverageLatency () const
{
return m_avgLatency;
}
std::uint64_t getPeakLatency () const
{
return m_peakLatency;
}
};
}
#endif

View File

@@ -0,0 +1,226 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/module/core/functional/Job.h>
#include <ripple/module/core/functional/JobTypeInfo.h>
namespace ripple
{
class JobTypes
{
public:
typedef std::map <JobType, JobTypeInfo> Map;
typedef Map::const_iterator const_iterator;
JobTypes ()
: m_unknown (jtINVALID, "invalid", 0, true, true, 0, 0)
{
int maxLimit = std::numeric_limits <int>::max ();
// Make a fetch pack for a peer
add (jtPACK, "makeFetchPack",
1, true, false, 0, 0);
// An old ledger has been accepted
add (jtPUBOLDLEDGER, "publishAcqLedger",
2, true, false, 10000, 15000);
// A validation from an untrusted source
add (jtVALIDATION_ut, "untrustedValidation",
maxLimit, true, false, 2000, 5000);
// A proof of work demand from another server
add (jtPROOFWORK, "proofOfWork",
maxLimit, true, false, 2000, 5000);
// A local transaction
add (jtTRANSACTION_l, "localTransaction",
maxLimit, true, false, 100, 500);
// A proposal from an untrusted source
add (jtPROPOSAL_ut, "untrustedProposal",
maxLimit, true, false, 500, 1250);
// Received data for a ledger we're acquiring
add (jtLEDGER_DATA, "ledgerData",
2, true, false, 0, 0);
// Update pathfinding requests
add (jtUPDATE_PF, "updatePaths",
maxLimit, true, false, 0, 0);
// A websocket command from the client
add (jtCLIENT, "clientCommand",
maxLimit, true, false, 2000, 5000);
// A websocket command from the client
add (jtRPC, "RPC",
maxLimit, false, false, 0, 0);
// A transaction received from the network
add (jtTRANSACTION, "transaction",
maxLimit, true, false, 250, 1000);
// A Score or Fetch of the UNL (DEPRECATED)
add (jtUNL, "unl",
1, true, false, 0, 0);
// Advance validated/acquired ledgers
add (jtADVANCE, "advanceLedger",
maxLimit, true, false, 0, 0);
// Publish a fully-accepted ledger
add (jtPUBLEDGER, "publishNewLedger",
maxLimit, true, false, 3000, 4500);
// Fetch a proposed set
add (jtTXN_DATA, "fetchTxnData",
1, true, false, 0, 0);
// Write-ahead logging
add (jtWAL, "writeAhead",
maxLimit, false, false, 1000, 2500);
// A validation from a trusted source
add (jtVALIDATION_t, "trustedValidation",
maxLimit, true, false, 500, 1500);
// Write out hashed objects
add (jtWRITE, "writeObjects",
maxLimit, false, false, 1750, 2500);
// Accept a consensus ledger
add (jtACCEPT, "acceptLedger",
maxLimit, false, false, 0, 0);
// A proposal from a trusted source
add (jtPROPOSAL_t, "trustedProposal",
maxLimit, false, false, 100, 500);
// Sweep for stale structures
add (jtSWEEP, "sweep",
maxLimit, true, false, 0, 0);
// NetworkOPs cluster peer report
add (jtNETOP_CLUSTER, "clusterReport",
1, true, false, 9999, 9999);
// NetworkOPs net timer processing
add (jtNETOP_TIMER, "heartbeat",
1, true, false, 999, 999);
// An administrative operation
add (jtADMIN, "administration",
maxLimit, true, false, 0, 0);
// The rest are special job types that are not dispatched
// by the job pool. The "limit" and "skip" attributes are
// not applicable to these types of jobs.
add (jtPEER, "peerCommand",
0, false, true, 200, 2500);
add (jtDISK, "diskAccess",
0, false, true, 500, 1000);
add (jtTXN_PROC, "processTransaction",
0, false, true, 0, 0);
add (jtOB_SETUP, "orderBookSetup",
0, false, true, 0, 0);
add (jtPATH_FIND, "pathFind",
0, false, true, 0, 0);
add (jtHO_READ, "nodeRead",
0, false, true, 0, 0);
add (jtHO_WRITE, "nodeWrite",
0, false, true, 0, 0);
add (jtGENERIC, "generic",
0, false, true, 0, 0);
add (jtNS_SYNC_READ, "SyncReadNode",
0, false, true, 0, 0);
add (jtNS_ASYNC_READ, "AsyncReadNode",
0, false, true, 0, 0);
add (jtNS_WRITE, "WriteNode",
0, false, true, 0, 0);
}
JobTypeInfo const& get (JobType jt) const
{
Map::const_iterator const iter (m_map.find (jt));
assert (iter != m_map.end ());
if (iter != m_map.end())
return iter->second;
return m_unknown;
}
JobTypeInfo const& getInvalid () const
{
return m_unknown;
}
const_iterator begin () const
{
return m_map.cbegin ();
}
const_iterator cbegin () const
{
return m_map.cbegin ();
}
const_iterator end () const
{
return m_map.cend ();
}
const_iterator cend () const
{
return m_map.cend ();
}
private:
void add(JobType jt, std::string name, int limit,
bool skip, bool special, std::uint64_t avgLatency, std::uint64_t peakLatency)
{
assert (m_map.find (jt) == m_map.end ());
std::pair<Map::iterator,bool> result (m_map.emplace (
std::piecewise_construct,
std::forward_as_tuple (jt),
std::forward_as_tuple (jt, name, limit, skip, special,
avgLatency, peakLatency)));
assert (result.second == true);
}
JobTypeInfo m_unknown;
Map m_map;
};
}

View File

@@ -0,0 +1,94 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace ripple {
LoadEvent::LoadEvent (LoadMonitor& monitor, const std::string& name, bool shouldStart)
: m_loadMonitor (monitor)
, m_isRunning (false)
, m_name (name)
, m_timeStopped (beast::RelativeTime::fromStartup())
, m_secondsWaiting (0)
, m_secondsRunning (0)
{
if (shouldStart)
start ();
}
LoadEvent::~LoadEvent ()
{
if (m_isRunning)
stop ();
}
std::string const& LoadEvent::name () const
{
return m_name;
}
double LoadEvent::getSecondsWaiting() const
{
return m_secondsWaiting;
}
double LoadEvent::getSecondsRunning() const
{
return m_secondsRunning;
}
double LoadEvent::getSecondsTotal() const
{
return m_secondsWaiting + m_secondsRunning;
}
void LoadEvent::reName (const std::string& name)
{
m_name = name;
}
void LoadEvent::start ()
{
beast::RelativeTime const currentTime (beast::RelativeTime::fromStartup());
// If we already called start, this call will replace the previous one.
if (m_isRunning)
{
m_secondsWaiting += (currentTime - m_timeStarted).inSeconds();
}
else
{
m_secondsWaiting += (currentTime - m_timeStopped).inSeconds();
m_isRunning = true;
}
m_timeStarted = currentTime;
}
void LoadEvent::stop ()
{
bassert (m_isRunning);
m_timeStopped = beast::RelativeTime::fromStartup();
m_secondsRunning += (m_timeStopped - m_timeStarted).inSeconds();
m_isRunning = false;
m_loadMonitor.addLoadSample (*this);
}
} // ripple

View File

@@ -0,0 +1,86 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_LOADEVENT_H
#define RIPPLE_LOADEVENT_H
#include <beast/chrono/RelativeTime.h>
namespace ripple {
class LoadMonitor;
// VFALCO NOTE What is the difference between a LoadEvent and a LoadMonitor?
// VFALCO TODO Rename LoadEvent to ScopedLoadSample
//
// This looks like a scoped elapsed time measuring class
//
class LoadEvent
{
public:
// VFALCO NOTE Why are these shared pointers? Wouldn't there be a
// piece of lifetime-managed calling code that can simply own
// the object?
//
// Why both kinds of containers?
//
typedef boost::shared_ptr <LoadEvent> pointer;
typedef std::unique_ptr <LoadEvent> autoptr;
public:
// VFALCO TODO remove the dependency on LoadMonitor. Is that possible?
LoadEvent (LoadMonitor& monitor,
const std::string& name,
bool shouldStart);
~LoadEvent ();
std::string const& name () const;
double getSecondsWaiting() const;
double getSecondsRunning() const;
double getSecondsTotal() const;
// VFALCO TODO rename this to setName () or setLabel ()
void reName (const std::string& name);
// Start the measurement. The constructor calls this automatically if
// shouldStart is true. If the operation is aborted, start() can be
// called again later.
//
void start ();
// Stops the measurement and reports the results. The time reported is
// measured from the last call to start.
//
void stop ();
private:
LoadMonitor& m_loadMonitor;
bool m_isRunning;
std::string m_name;
// VFALCO TODO Replace these with chrono
beast::RelativeTime m_timeStopped;
beast::RelativeTime m_timeStarted;
double m_secondsWaiting;
double m_secondsRunning;
};
} // ripple
#endif

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_LOADFEETRACK_H_INCLUDED
#define RIPPLE_LOADFEETRACK_H_INCLUDED
namespace ripple {
/** Manages the current fee schedule.
The "base" fee is the cost to send a reference transaction under no load,
expressed in millionths of one XRP.
The "load" fee is how much the local server currently charges to send a
reference transaction. This fee fluctuates based on the load of the
server.
*/
// VFALCO TODO Rename "load" to "current".
class LoadFeeTrack
{
public:
/** Create a new tracker.
*/
static LoadFeeTrack* New (beast::Journal journal);
virtual ~LoadFeeTrack () { }
// Scale from fee units to millionths of a ripple
virtual std::uint64_t scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee,
std::uint32_t referenceFeeUnits) = 0;
// Scale using load as well as base rate
virtual std::uint64_t scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee,
std::uint32_t referenceFeeUnits,
bool bAdmin) = 0;
virtual void setRemoteFee (std::uint32_t) = 0;
virtual std::uint32_t getRemoteFee () = 0;
virtual std::uint32_t getLocalFee () = 0;
virtual std::uint32_t getClusterFee () = 0;
virtual std::uint32_t getLoadBase () = 0;
virtual std::uint32_t getLoadFactor () = 0;
virtual Json::Value getJson (std::uint64_t baseFee, std::uint32_t referenceFeeUnits) = 0;
virtual void setClusterFee (std::uint32_t) = 0;
virtual bool raiseLocalFee () = 0;
virtual bool lowerLocalFee () = 0;
virtual bool isLoadedLocal () = 0;
virtual bool isLoadedCluster () = 0;
};
} // ripple
#endif

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/unit_test/suite.h>
namespace ripple {
LoadFeeTrack* LoadFeeTrack::New (beast::Journal journal)
{
return new LoadFeeTrackImp (journal);
}
//------------------------------------------------------------------------------
class LoadFeeTrack_test : public beast::unit_test::suite
{
public:
void run ()
{
Config d; // get a default configuration object
LoadFeeTrackImp l;
expect (l.scaleFeeBase (10000, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 10000);
expect (l.scaleFeeLoad (10000, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE, false) == 10000);
expect (l.scaleFeeBase (1, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 1);
expect (l.scaleFeeLoad (1, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE, false) == 1);
// Check new default fee values give same fees as old defaults
expect (l.scaleFeeBase (d.FEE_DEFAULT, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 10);
expect (l.scaleFeeBase (d.FEE_ACCOUNT_RESERVE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 200 * SYSTEM_CURRENCY_PARTS);
expect (l.scaleFeeBase (d.FEE_OWNER_RESERVE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 50 * SYSTEM_CURRENCY_PARTS);
expect (l.scaleFeeBase (d.FEE_NICKNAME_CREATE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 1000);
expect (l.scaleFeeBase (d.FEE_OFFER, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 10);
expect (l.scaleFeeBase (d.FEE_CONTRACT_OPERATION, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 1);
}
};
BEAST_DEFINE_TESTSUITE(LoadFeeTrack,ripple_core,ripple);
} // ripple

View File

@@ -0,0 +1,236 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_LOADFEETRACKIMP_H_INCLUDED
#define RIPPLE_LOADFEETRACKIMP_H_INCLUDED
#include <ripple/common/jsonrpc_fields.h>
namespace ripple {
class LoadFeeTrackImp : public LoadFeeTrack
{
public:
explicit LoadFeeTrackImp (beast::Journal journal = beast::Journal())
: m_journal (journal)
, mLocalTxnLoadFee (lftNormalFee)
, mRemoteTxnLoadFee (lftNormalFee)
, mClusterTxnLoadFee (lftNormalFee)
, raiseCount (0)
{
}
// Scale using load as well as base rate
std::uint64_t scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee, std::uint32_t referenceFeeUnits, bool bAdmin)
{
static std::uint64_t midrange (0x00000000FFFFFFFF);
bool big = (fee > midrange);
if (big) // big fee, divide first to avoid overflow
fee /= baseFee;
else // normal fee, multiply first for accuracy
fee *= referenceFeeUnits;
std::uint32_t feeFactor = std::max (mLocalTxnLoadFee, mRemoteTxnLoadFee);
// Let admins pay the normal fee until the local load exceeds four times the remote
std::uint32_t uRemFee = std::max(mRemoteTxnLoadFee, mClusterTxnLoadFee);
if (bAdmin && (feeFactor > uRemFee) && (feeFactor < (4 * uRemFee)))
feeFactor = uRemFee;
{
ScopedLockType sl (mLock);
fee = mulDiv (fee, feeFactor, lftNormalFee);
}
if (big) // Fee was big to start, must now multiply
fee *= referenceFeeUnits;
else // Fee was small to start, mst now divide
fee /= baseFee;
return fee;
}
// Scale from fee units to millionths of a ripple
std::uint64_t scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee, std::uint32_t referenceFeeUnits)
{
return mulDiv (fee, referenceFeeUnits, baseFee);
}
std::uint32_t getRemoteFee ()
{
ScopedLockType sl (mLock);
return mRemoteTxnLoadFee;
}
std::uint32_t getLocalFee ()
{
ScopedLockType sl (mLock);
return mLocalTxnLoadFee;
}
std::uint32_t getLoadBase ()
{
return lftNormalFee;
}
std::uint32_t getLoadFactor ()
{
ScopedLockType sl (mLock);
return std::max(mClusterTxnLoadFee, std::max (mLocalTxnLoadFee, mRemoteTxnLoadFee));
}
void setClusterFee (std::uint32_t fee)
{
ScopedLockType sl (mLock);
mClusterTxnLoadFee = fee;
}
std::uint32_t getClusterFee ()
{
ScopedLockType sl (mLock);
return mClusterTxnLoadFee;
}
bool isLoadedLocal ()
{
// VFALCO TODO This could be replaced with a SharedData and
// using a read/write lock instead of a critical section.
//
// NOTE This applies to all the locking in this class.
//
//
ScopedLockType sl (mLock);
return (raiseCount != 0) || (mLocalTxnLoadFee != lftNormalFee);
}
bool isLoadedCluster ()
{
// VFALCO TODO This could be replaced with a SharedData and
// using a read/write lock instead of a critical section.
//
// NOTE This applies to all the locking in this class.
//
//
ScopedLockType sl (mLock);
return (raiseCount != 0) || (mLocalTxnLoadFee != lftNormalFee) || (mClusterTxnLoadFee != lftNormalFee);
}
void setRemoteFee (std::uint32_t f)
{
ScopedLockType sl (mLock);
mRemoteTxnLoadFee = f;
}
bool raiseLocalFee ()
{
ScopedLockType sl (mLock);
if (++raiseCount < 2)
return false;
std::uint32_t origFee = mLocalTxnLoadFee;
if (mLocalTxnLoadFee < mRemoteTxnLoadFee) // make sure this fee takes effect
mLocalTxnLoadFee = mRemoteTxnLoadFee;
mLocalTxnLoadFee += (mLocalTxnLoadFee / lftFeeIncFraction); // increment by 1/16th
if (mLocalTxnLoadFee > lftFeeMax)
mLocalTxnLoadFee = lftFeeMax;
if (origFee == mLocalTxnLoadFee)
return false;
m_journal.debug << "Local load fee raised from " << origFee << " to " << mLocalTxnLoadFee;
return true;
}
bool lowerLocalFee ()
{
ScopedLockType sl (mLock);
std::uint32_t origFee = mLocalTxnLoadFee;
raiseCount = 0;
mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/4
if (mLocalTxnLoadFee < lftNormalFee)
mLocalTxnLoadFee = lftNormalFee;
if (origFee == mLocalTxnLoadFee)
return false;
m_journal.debug << "Local load fee lowered from " << origFee << " to " << mLocalTxnLoadFee;
return true;
}
Json::Value getJson (std::uint64_t baseFee, std::uint32_t referenceFeeUnits)
{
Json::Value j (Json::objectValue);
{
ScopedLockType sl (mLock);
// base_fee = The cost to send a "reference" transaction under no load, in millionths of a Ripple
j[jss::base_fee] = Json::Value::UInt (baseFee);
// load_fee = The cost to send a "reference" transaction now, in millionths of a Ripple
j[jss::load_fee] = Json::Value::UInt (
mulDiv (baseFee, std::max (mLocalTxnLoadFee, mRemoteTxnLoadFee), lftNormalFee));
}
return j;
}
private:
// VFALCO TODO Move this function to some "math utilities" file
// compute (value)*(mul)/(div) - avoid overflow but keep precision
std::uint64_t mulDiv (std::uint64_t value, std::uint32_t mul, std::uint64_t div)
{
// VFALCO TODO replace with beast::literal64bitUnsigned ()
//
static std::uint64_t boundary = (0x00000000FFFFFFFF);
if (value > boundary) // Large value, avoid overflow
return (value / div) * mul;
else // Normal value, preserve accuracy
return (value * mul) / div;
}
private:
static const int lftNormalFee = 256; // 256 is the minimum/normal load factor
static const int lftFeeIncFraction = 4; // increase fee by 1/4
static const int lftFeeDecFraction = 4; // decrease fee by 1/4
static const int lftFeeMax = lftNormalFee * 1000000;
beast::Journal m_journal;
typedef RippleMutex LockType;
typedef std::lock_guard <LockType> ScopedLockType;
LockType mLock;
std::uint32_t mLocalTxnLoadFee; // Scale factor, lftNormalFee = normal fee
std::uint32_t mRemoteTxnLoadFee; // Scale factor, lftNormalFee = normal fee
std::uint32_t mClusterTxnLoadFee; // Scale factor, lftNormalFee = normal fee
int raiseCount;
};
} // ripple
#endif

View File

@@ -0,0 +1,241 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace ripple {
/*
TODO
----
- Use Journal for logging
*/
//------------------------------------------------------------------------------
LoadMonitor::Stats::Stats()
: count (0)
, latencyAvg (0)
, latencyPeak (0)
, isOverloaded (false)
{
}
//------------------------------------------------------------------------------
SETUP_LOG (LoadMonitor)
LoadMonitor::LoadMonitor ()
: mCounts (0)
, mLatencyEvents (0)
, mLatencyMSAvg (0)
, mLatencyMSPeak (0)
, mTargetLatencyAvg (0)
, mTargetLatencyPk (0)
, mLastUpdate (UptimeTimer::getInstance ().getElapsedSeconds ())
{
}
// VFALCO NOTE WHY do we need "the mutex?" This dependence on
// a hidden global, especially a synchronization primitive,
// is a flawed design.
// It's not clear exactly which data needs to be protected.
//
// call with the mutex
void LoadMonitor::update ()
{
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
// VFALCO TODO stop returning from the middle of functions.
if (now == mLastUpdate) // current
return;
// VFALCO TODO Why 8?
if ((now < mLastUpdate) || (now > (mLastUpdate + 8)))
{
// way out of date
mCounts = 0;
mLatencyEvents = 0;
mLatencyMSAvg = 0;
mLatencyMSPeak = 0;
mLastUpdate = now;
// VFALCO TODO don't return from the middle...
return;
}
// do exponential decay
/*
David:
"Imagine if you add 10 to something every second. And you
also reduce it by 1/4 every second. It will "idle" at 40,
correponding to 10 counts per second."
*/
do
{
++mLastUpdate;
mCounts -= ((mCounts + 3) / 4);
mLatencyEvents -= ((mLatencyEvents + 3) / 4);
mLatencyMSAvg -= (mLatencyMSAvg / 4);
mLatencyMSPeak -= (mLatencyMSPeak / 4);
}
while (mLastUpdate < now);
}
void LoadMonitor::addCount ()
{
ScopedLockType sl (mLock);
update ();
++mCounts;
}
void LoadMonitor::addLatency (int latency)
{
// VFALCO NOTE Why does 1 become 0?
if (latency == 1)
latency = 0;
ScopedLockType sl (mLock);
update ();
++mLatencyEvents;
mLatencyMSAvg += latency;
mLatencyMSPeak += latency;
// Units are quarters of a millisecond
int const latencyPeak = mLatencyEvents * latency * 4;
if (mLatencyMSPeak < latencyPeak)
mLatencyMSPeak = latencyPeak;
}
std::string LoadMonitor::printElapsed (double seconds)
{
std::stringstream ss;
ss << (std::size_t (seconds * 1000 + 0.5)) << " ms";
return ss.str();
}
void LoadMonitor::addLoadSample (LoadEvent const& sample)
{
std::string const& name (sample.name());
beast::RelativeTime const latency (sample.getSecondsTotal());
if (latency.inSeconds() > 0.5)
{
WriteLog ((latency.inSeconds() > 1.0) ? lsWARNING : lsINFO, LoadMonitor)
<< "Job: " << name << " ExecutionTime: " << printElapsed (sample.getSecondsRunning()) <<
" WaitingTime: " << printElapsed (sample.getSecondsWaiting());
}
// VFALCO NOTE Why does 1 become 0?
std::size_t latencyMilliseconds (latency.inMilliseconds());
if (latencyMilliseconds == 1)
latencyMilliseconds = 0;
ScopedLockType sl (mLock);
update ();
++mCounts;
++mLatencyEvents;
mLatencyMSAvg += latencyMilliseconds;
mLatencyMSPeak += latencyMilliseconds;
// VFALCO NOTE Why are we multiplying by 4?
int const latencyPeak = mLatencyEvents * latencyMilliseconds * 4;
if (mLatencyMSPeak < latencyPeak)
mLatencyMSPeak = latencyPeak;
}
/* Add multiple samples
@param count The number of samples to add
@param latencyMS The total number of milliseconds
*/
void LoadMonitor::addSamples (int count, std::chrono::milliseconds latency)
{
ScopedLockType sl (mLock);
update ();
mCounts += count;
mLatencyEvents += count;
mLatencyMSAvg += latency.count();
mLatencyMSPeak += latency.count();
int const latencyPeak = mLatencyEvents * latency.count() * 4 / count;
if (mLatencyMSPeak < latencyPeak)
mLatencyMSPeak = latencyPeak;
}
void LoadMonitor::setTargetLatency (std::uint64_t avg, std::uint64_t pk)
{
mTargetLatencyAvg = avg;
mTargetLatencyPk = pk;
}
bool LoadMonitor::isOverTarget (std::uint64_t avg, std::uint64_t peak)
{
return (mTargetLatencyPk && (peak > mTargetLatencyPk)) ||
(mTargetLatencyAvg && (avg > mTargetLatencyAvg));
}
bool LoadMonitor::isOver ()
{
ScopedLockType sl (mLock);
update ();
if (mLatencyEvents == 0)
return 0;
return isOverTarget (mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4));
}
LoadMonitor::Stats LoadMonitor::getStats ()
{
Stats stats;
ScopedLockType sl (mLock);
update ();
stats.count = mCounts / 4;
if (mLatencyEvents == 0)
{
stats.latencyAvg = 0;
stats.latencyPeak = 0;
}
else
{
stats.latencyAvg = mLatencyMSAvg / (mLatencyEvents * 4);
stats.latencyPeak = mLatencyMSPeak / (mLatencyEvents * 4);
}
stats.isOverloaded = isOverTarget (stats.latencyAvg, stats.latencyPeak);
return stats;
}
} // ripple

View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_LOADMONITOR_H_INCLUDED
#define RIPPLE_LOADMONITOR_H_INCLUDED
namespace ripple {
// Monitors load levels and response times
// VFALCO TODO Rename this. Having both LoadManager and LoadMonitor is confusing.
//
class LoadMonitor
{
public:
LoadMonitor ();
void addCount ();
void addLatency (int latency);
void addLoadSample (LoadEvent const& sample);
void addSamples (int count, std::chrono::milliseconds latency);
void setTargetLatency (std::uint64_t avg, std::uint64_t pk);
bool isOverTarget (std::uint64_t avg, std::uint64_t peak);
// VFALCO TODO make this return the values in a struct.
struct Stats
{
Stats();
std::uint64_t count;
std::uint64_t latencyAvg;
std::uint64_t latencyPeak;
bool isOverloaded;
};
Stats getStats ();
bool isOver ();
private:
static std::string printElapsed (double seconds);
void update ();
typedef RippleMutex LockType;
typedef std::lock_guard <LockType> ScopedLockType;
LockType mLock;
std::uint64_t mCounts;
int mLatencyEvents;
std::uint64_t mLatencyMSAvg;
std::uint64_t mLatencyMSPeak;
std::uint64_t mTargetLatencyAvg;
std::uint64_t mTargetLatencyPk;
int mLastUpdate;
};
} // ripple
#endif