//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== // // 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 // VFALCO TODO Convert this to a SharedSingleton to prevent exit leaks // Config& getConfig () { static Config config; return config; } Config::Config () : m_rpcPort (5001) , SSL_CONTEXT (boost::asio::ssl::context::sslv23) { // // Defaults // TESTNET = false; NETWORK_START_TIME = 1319844908; RPC_SECURE = 0; WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT; WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT; WEBSOCKET_PUBLIC_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 ("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; 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; PATH_SEARCH_SIZE = DEFAULT_PATH_SEARCH_SIZE; ACCOUNT_PROBE_MAX = 10; VALIDATORS_SITE = DEFAULT_VALIDATORS_SITE; SSL_VERIFY = true; ELB_SUPPORT = false; RUN_STANDALONE = false; START_UP = NORMAL; //-------------------------------------------------------------------------- // // VFALCO NOTE Clean area // peerListeningPort = SYSTEM_PEER_PORT; peerPROXYListeningPort = 0; // // // //-------------------------------------------------------------------------- } void Config::setup (const std::string& strConf, bool bTestNet, 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. // TESTNET = bTestNet; QUIET = bQuiet; NODE_SIZE = 0; // TESTNET forces a "testnet-" prefix on the conf file and db directory. strDbPath = TESTNET ? "testnet-db" : "db"; strConfFile = boost::str (boost::format (TESTNET ? "testnet-%s" : "%s") % (strConf.empty () ? CONFIG_FILE_NAME : strConf)); VALIDATORS_BASE = boost::str (boost::format (TESTNET ? "testnet-%s" : "%s") % VALIDATORS_FILE_NAME); VALIDATORS_URI = boost::str (boost::format ("/%s") % VALIDATORS_BASE); if (TESTNET) { SIGN_TRANSACTION = HashPrefix::txSignTestnet; SIGN_VALIDATION = HashPrefix::validationTestnet; SIGN_PROPOSAL = HashPrefix::proposalTestnet; } else { SIGN_TRANSACTION = HashPrefix::txSign; SIGN_VALIDATION = HashPrefix::validation; SIGN_PROPOSAL = HashPrefix::proposal; } if (TESTNET) Base58::setCurrentAlphabet (Base58::getTestnetAlphabet ()); if (!strConf.empty ()) { // --conf= : 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)); } } if (SSL_VERIFY_FILE.empty ()) { SSL_CONTEXT.set_default_verify_paths (ec); if (ec && SSL_VERIFY_DIR.empty ()) throw std::runtime_error (boost::str (boost::format ("Failed to set_default_verify_paths: %s") % ec.message ())); } else SSL_CONTEXT.load_verify_file (SSL_VERIFY_FILE); if (!SSL_VERIFY_DIR.empty ()) { SSL_CONTEXT.add_verify_path (SSL_VERIFY_DIR, ec); if (ec) throw std::runtime_error (boost::str (boost::format ("Failed to add verify path: %s") % ec.message ())); } // 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)); } 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 (ifsConfig)), std::istreambuf_iterator ()); 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; // SectionEntriesPrint(&VALIDATORS, SECTION_VALIDATORS); } smtTmp = SectionEntries (secConfig, SECTION_CLUSTER_NODES); if (smtTmp) { CLUSTER_NODES = *smtTmp; // SectionEntriesPrint(&CLUSTER_NODES, SECTION_CLUSTER_NODES); } smtTmp = SectionEntries (secConfig, SECTION_IPS); if (smtTmp) { IPS = *smtTmp; // SectionEntriesPrint(&IPS, SECTION_IPS); } 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 = lexicalCastThrow (strTemp); smtTmp = SectionEntries (secConfig, SECTION_RPC_ADMIN_ALLOW); if (smtTmp) { RPC_ADMIN_ALLOW = *smtTmp; } (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); //--------------------------------------- // // 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 = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_PEER_PROXY_PORT, strTemp)) { peerPROXYListeningPort = lexicalCastThrow (strTemp); if (peerPROXYListeningPort != 0 && peerPROXYListeningPort == peerListeningPort) FatalError ("Peer and proxy listening ports can't be the same.", __FILE__, __LINE__); } else { peerPROXYListeningPort = 0; } // // VFALCO END CLEAN // //--------------------------------------- if (SectionSingleB (secConfig, SECTION_RPC_PORT, strTemp)) m_rpcPort = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, "ledger_creator" , strTemp)) LEDGER_CREATOR = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp)) RPC_ALLOW_REMOTE = lexicalCastThrow (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 = lexicalCastThrow (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 = lexicalCastThrow (strTemp); (void) SectionSingleB (secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP); if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PORT, strTemp)) WEBSOCKET_PORT = lexicalCastThrow (strTemp); (void) SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP); if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp)) WEBSOCKET_PUBLIC_PORT = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_WEBSOCKET_SECURE, strTemp)) WEBSOCKET_SECURE = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_SECURE, strTemp)) WEBSOCKET_PUBLIC_SECURE = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PING_FREQ, strTemp)) WEBSOCKET_PING_FREQ = lexicalCastThrow (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 = lexicalCastThrow (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 = lexicalCastThrow (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, lexicalCastThrow (strTemp)); if (SectionSingleB (secConfig, SECTION_PEER_START_MAX, strTemp)) PEER_START_MAX = std::max (1, lexicalCastThrow (strTemp)); if (SectionSingleB (secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp)) PEER_CONNECT_LOW_WATER = std::max (1, lexicalCastThrow (strTemp)); if (SectionSingleB (secConfig, SECTION_NETWORK_QUORUM, strTemp)) NETWORK_QUORUM = std::max (0, lexicalCastThrow (strTemp)); if (SectionSingleB (secConfig, SECTION_VALIDATION_QUORUM, strTemp)) VALIDATION_QUORUM = std::max (0, lexicalCastThrow (strTemp)); if (SectionSingleB (secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp)) FEE_ACCOUNT_RESERVE = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_FEE_OWNER_RESERVE, strTemp)) FEE_OWNER_RESERVE = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_FEE_NICKNAME_CREATE, strTemp)) FEE_NICKNAME_CREATE = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_FEE_OFFER, strTemp)) FEE_OFFER = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_FEE_DEFAULT, strTemp)) FEE_DEFAULT = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_FEE_OPERATION, strTemp)) FEE_CONTRACT_OPERATION = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_LEDGER_HISTORY, strTemp)) { boost::to_lower (strTemp); if (strTemp == "none") LEDGER_HISTORY = 0; else if (strTemp == "full") LEDGER_HISTORY = 1000000000u; else LEDGER_HISTORY = lexicalCastThrow (strTemp); } if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_SIZE, strTemp)) PATH_SEARCH_SIZE = lexicalCastThrow (strTemp); if (SectionSingleB (secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp)) ACCOUNT_PROBE_MAX = lexicalCastThrow (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; } } } 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, { 8192, 65536, 262144, 2097152, 0 } }, { siNodeCacheAge, { 30, 60, 90, 300, 900 } }, { siSLECacheSize, { 4096, 8192, 16384, 65536, 0 } }, { siSLECacheAge, { 30, 60, 90, 120, 300 } }, { siLedgerSize, { 32, 128, 256, 2048, 0 } }, { siLedgerAge, { 30, 90, 180, 300, 900 } }, { siHashNodeDBCache, { 4, 12, 24, 32, 64 } }, { siTxnDBCache, { 4, 12, 24, 32, 32 } }, { siLgrDBCache, { 4, 8, 16, 16, 16 } }, }; 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; } void Config::setRpcIpAndOptionalPort (std::string const& newAddress) { String const s (newAddress.c_str ()); int const colonPosition = s.lastIndexOfChar (':'); if (colonPosition != -1) { String const ipPart = s.substring (0, colonPosition); String const portPart = s.substring (colonPosition + 1, s.length ()); setRpcIP (ipPart.toRawUTF8 ()); setRpcPort (portPart.getIntValue ()); } else { setRpcIP (newAddress); } }