diff --git a/SConstruct b/SConstruct index 83dd04ef4e..8362a9383b 100644 --- a/SConstruct +++ b/SConstruct @@ -106,7 +106,7 @@ env.Append(LINKFLAGS = ['-rdynamic', '-pthread']) env.Append(CCFLAGS = ['-pthread', '-Wall', '-Wno-sign-compare', '-Wno-char-subscripts', '-DSQLITE_THREADSAFE=1']) env.Append(CXXFLAGS = ['-O0', '-pthread', '-Wno-invalid-offsetof', '-Wformat']+BOOSTFLAGS+DEBUGFLAGS) -if (GCC_VERSION[0] > 4 or (GCC_VERSION[0] == 4 and GCC_VERSION[1] >= 7)): +if (int(GCC_VERSION[0]) > 4 or (int(GCC_VERSION[0]) == 4 and int(GCC_VERSION[1]) >= 7)): env.Append(CXXFLAGS = ['-std=c++11']) if OSX: diff --git a/newcoin.vcxproj b/newcoin.vcxproj index fbd79cb56d..07ca73e105 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -101,6 +101,7 @@ + @@ -175,6 +176,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 961347ae6f..483cb0c2d0 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -249,6 +249,9 @@ Source Files + + Source Files + Source Files @@ -363,6 +366,9 @@ Source Files + + Source Files + diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj index 843c6f9dcf..d3e9aff5c2 100644 --- a/ripple2010.vcxproj +++ b/ripple2010.vcxproj @@ -175,6 +175,7 @@ + diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters index 0ef380379d..bbdf822554 100644 --- a/ripple2010.vcxproj.filters +++ b/ripple2010.vcxproj.filters @@ -243,6 +243,9 @@ Source Files + + Source Files + Source Files diff --git a/rippled-example.cfg b/rippled-example.cfg index 1d5fa6d73e..fa5690e56e 100644 --- a/rippled-example.cfg +++ b/rippled-example.cfg @@ -17,7 +17,7 @@ # # Example: debug.log # -# [validators]: +# [validators] # List of nodes to always accept as validators. Nodes are specified by domain # or public key. # @@ -32,7 +32,7 @@ # n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5 # n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe # -# [validators_file]: +# [validators_file] # Path to file contain a list of nodes to always accept as validators. Use # this to specify a file other than this file to manage your validators list. # @@ -51,13 +51,13 @@ # C:/home/johndoe/ripple/validators.txt # /home/johndoe/ripple/validators.txt # -# [validators_site]: +# [validators_site] # Specifies where to find validators.txt for UNL boostrapping and RPC # unl_network command. # # Example: ripple.com # -# [ips]: +# [ips] # List of ips where the Ripple protocol is served. For a starter list, you # can copy entries from: https://ripple.com/ripple.txt # @@ -80,38 +80,38 @@ # time.nist.gov # pool.ntp.org # -# [peer_ip]: +# [peer_ip] # IP address or domain to bind to allow external connections from peers. # Defaults to not binding, which disallows external connections from peers. # # Examples: 0.0.0.0 - Bind on all interfaces. # -# [peer_port]: +# [peer_port] # If peer_ip is supplied, corresponding port to bind to for peer connections. # -# [peer_private]: +# [peer_private] # 0 or 1. # 0: request peers to broadcast your address. [default] # 1: request peers not broadcast your address. # -# [rpc_ip]: +# [rpc_ip] # IP address or domain to bind to allow insecure RPC connections. # Defaults to not binding, which disallows RPC connections. # -# [rpc_port]: +# [rpc_port] # If rpc_ip is supplied, corresponding port to bind to for peer connections. # -# [rpc_allow_remote]: +# [rpc_allow_remote] # 0 or 1. # 0: Allow RPC connections only from 127.0.0.1. [default] # 1: Allow RPC connections from any IP. # -# [rpc_admin_allow]: +# [rpc_admin_allow] # Specify an list of IP addresses allowed to have admin access. One per line. # # Defaults to 127.0.0.1. # -# [rpc_user]: +# [rpc_user] # As a server, require a this user to specified and require rpc_password to # be checked for RPC access via the rpc_ip and rpc_port. The user and password # must be specified via HTTP's basic authentication method. @@ -119,7 +119,7 @@ # As a client, supply this to the server via HTTP's basic authentication # method. # -# [rpc_password]: +# [rpc_password] # As a server, require a this password to specified and require rpc_user to # be checked for RPC access via the rpc_ip and rpc_port. The user and password # must be specified via HTTP's basic authentication method. @@ -127,7 +127,7 @@ # As a client, supply this to the server via HTTP's basic authentication # method. # -# [rpc_admin_user]: +# [rpc_admin_user] # As a server, require this as the admin user to be specified. Also, require # rpc_admin_user and rpc_admin_password to be checked for RPC admin functions. # The request must specify these as the admin_user and admin_password in the @@ -135,7 +135,7 @@ # # As a client, supply this to the server in the request object. # -# [rpc_admin_password]: +# [rpc_admin_password] # As a server, require this as the admin pasword to be specified. Also, # require rpc_admin_user and rpc_admin_password to be checked for RPC admin # functions. The request must specify these as the admin_user and @@ -143,12 +143,12 @@ # # As a client, supply this to the server in the request object. # -# [validation_quorum]: +# [validation_quorum] # Sets the minimum number of trusted validations a ledger must have before # the server considers it fully validated. Note that if you are validating, # your validation counts. # -# [websocket_public_ip]: +# [websocket_public_ip] # IP address or domain to bind to allow untrusted connections from clients. # In the future, this option will go away and the peer_ip will accept # websocket client connections. @@ -156,7 +156,7 @@ # Examples: 0.0.0.0 - Bind on all interfaces. # 127.0.0.1 - Bind on localhost interface. Only local programs may connect. # -# [websocket_public_port]: +# [websocket_public_port] # Port to bind to allow untrusted connections from clients. In the future, # this option will go away and the peer_ip will accept websocket client # connections. @@ -172,14 +172,14 @@ # reference client currently shares secrets with its server, this should be # enabled. # -# [websocket_ip]: +# [websocket_ip] # IP address or domain to bind to allow trusted ADMIN connections from backend # applications. # # Examples: 0.0.0.0 - Bind on all interfaces. # 127.0.0.1 - Bind on localhost interface. Only local programs may connect. # -# [websocket_port]: +# [websocket_port] # Port to bind to allow trusted ADMIN connections from backend applications. # # [websocket_secure] @@ -188,14 +188,14 @@ # 1: Provide ws and wss service for websocket_ip/websocket_port # 2: Provide wss service for websocket_ip/websocket_port. # -# [websocket_ssl_key]: +# [websocket_ssl_key] # Specify the filename holding the SSL key in PEM format. # -# [websocket_ssl_cert]: +# [websocket_ssl_cert] # Specify the path to the SSL certificate file in PEM format. # This is not needed if the chain includes it. # -# [websocket_ssl_chain]: +# [websocket_ssl_chain] # If you need a certificate chain, specify the path to the certificate chain # here. The chain may include the end certificate. # @@ -205,7 +205,7 @@ # that the server will accept for verifying HTTP servers. Used only for # outbound HTTPS client connections. # -# [validation_seed]: +# [validation_seed] # To perform validation, this section should contain either a validation seed # or key. The validation seed is used to generate the validation # public/private key pair. To obtain a validation seed, use the @@ -214,7 +214,7 @@ # Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE # shfArahZT9Q9ckTf3s1psJ7C7qzVN # -# [node_seed]: +# [node_seed] # This is used for clustering. To force a particular node seed or key, the # key can be set here. The format is the same as the validation_seed field. # To obtain a validation seed, use the validation_create command. @@ -222,19 +222,19 @@ # Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE # shfArahZT9Q9ckTf3s1psJ7C7qzVN # -# [node_size]: +# [node_size] # Tunes the servers based on the expected load and available memory. Legal # sizes are "tiny", "small", "medium", "large", and "huge". We recommend # you start at the default and raise the setting if you have extra memory. # The default is "tiny". # -# [cluster_nodes]: +# [cluster_nodes] # To extend full trust to other nodes, place their node public keys here. # Generally, you should only do this for nodes under common administration. # Node public keys start with an 'n'. To give a node a name for identification # place a space after the public key and then the name. # -# [ledger_history]: +# [ledger_history] # The number of past ledgers to acquire on server startup and the minimum to # maintain while running. # @@ -244,7 +244,7 @@ # # The default is: 256 # -# [database_path]: +# [database_path] # Full path of database directory. # # [path_search_size] @@ -253,10 +253,12 @@ # # The default is: 5 # -# [rpc_startup]: +# [rpc_startup] # Specify a list of RPC commands to run at startup. # -# Example: { "command" : "server_info" } +# Examples: +# { "command" : "server_info" } +# { "command" : "log_level", "partition" : "ripplecalc", "severity" : "trace" } # # Allow other peers to connect to this server. diff --git a/src/cpp/json/json_value.cpp b/src/cpp/json/json_value.cpp index b1b1a5221f..63b48020e6 100644 --- a/src/cpp/json/json_value.cpp +++ b/src/cpp/json/json_value.cpp @@ -100,8 +100,7 @@ public: static ValueAllocator *&valueAllocator() { - static DefaultValueAllocator defaultAllocator; - static ValueAllocator *valueAllocator = &defaultAllocator; + static ValueAllocator *valueAllocator = new DefaultValueAllocator; return valueAllocator; } diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 2232fae789..7546aa9cfa 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -257,134 +258,77 @@ std::string STAmount::createHumanCurrency(const uint160& uCurrency) return sCurrency; } -// Assumes trusted input. bool STAmount::setValue(const std::string& sAmount) { // Note: mIsNative and mCurrency must be set already! - uint64 uValue; - int iOffset; - size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : "."); - size_t uExp = uDecimal == std::string::npos ? sAmount.find_first_of("e") : std::string::npos; - bool bInteger = uDecimal == std::string::npos && uExp == std::string::npos; - mIsNegative = false; - if (bInteger) + static boost::regex reNumber("\\`([+-]?)(\\d*)(\\.(\\d*))?([eE]([+-]?)(\\d+))?\\'"); + boost::smatch smMatch; + + if (!boost::regex_match(sAmount, smMatch, reNumber)) { - // Integer input: does not necessarily mean native. + cLog(lsWARNING) << "Number not valid: \"" << sAmount << "\""; + return false; + } - try + // Match fields: 0 = whole input, 1 = sign, 2 = integer portion, 3 = whole fraction (with '.') + // 4 = fraction (without '.'), 5 = whole exponent (with 'e'), 6 = exponent sign, 7 = exponent number + + try + { + if ((smMatch[2].length() + smMatch[4].length()) > 18) { - int64 a = sAmount.empty() ? 0 : lexical_cast_st(sAmount); - if (a >= 0) - { - uValue = static_cast(a); - } - else - { - uValue = static_cast(-a); - mIsNegative = true; - } - - } - catch (...) - { - cLog(lsINFO) << "Bad integer amount: " << sAmount; - + cLog(lsWARNING) << "Overlong number: " << sAmount; return false; } - iOffset = 0; - } - else if (uExp != std::string::npos) - { - // e input - try + mIsNegative = (smMatch[1].matched && (smMatch[1] == "-")); + + if (!smMatch[4].matched) // integer only { - int64 iInteger = uExp ? lexical_cast_st(sAmount.substr(0, uExp)) : 0; - if (iInteger >= 0) - { - uValue = static_cast(iInteger); - } + mValue = lexical_cast_s(smMatch[2]); + mOffset = 0; + } + else + { // integer and fraction + mValue = lexical_cast_s(smMatch[2] + smMatch[4]); + mOffset = -(smMatch[4].length()); + } + + if (smMatch[5].matched) + { // we have an exponent + if (smMatch[6].matched && (smMatch[6] == "-")) + mOffset -= lexical_cast_s(smMatch[7]); else - { - uValue = static_cast(-iInteger); - mIsNegative = true; - } - - iOffset = lexical_cast_st(sAmount.substr(uExp+1)); - } - catch (...) - { - cLog(lsINFO) << "Bad e amount: " << sAmount; - - return false; + mOffset += lexical_cast_s(smMatch[7]); } } - else + catch (...) { - // Float input: has a decimal - - // Example size decimal size-decimal offset - // ^1 2 0 2 -1 - // 123^ 4 3 1 0 - // 1^23 4 1 3 -2 - try - { - iOffset = -int(sAmount.size() - uDecimal - 1); - - // Issolate integer and fraction. - uint64 uInteger; - int64 iInteger = uDecimal ? lexical_cast_st(sAmount.substr(0, uDecimal)) : 0; - if (iInteger >= 0) - { - uInteger = static_cast(iInteger); - } - else - { - uInteger = static_cast(-iInteger); - mIsNegative = true; - } - - uint64 uFraction = iOffset ? lexical_cast_st(sAmount.substr(uDecimal+1)) : 0; - - // Scale the integer portion to the same offset as the fraction. - uValue = uInteger; - for (int i = -iOffset; i--;) - uValue *= 10; - - // Add in the fraction. - uValue += uFraction; - } - catch (...) - { - cLog(lsINFO) << "Bad float amount: " << sAmount; - - return false; - } + cLog(lsWARNING) << "Number not parsed: \"" << sAmount << "\""; + return false; } + cLog(lsTRACE) << "Float \"" << sAmount << "\" parsed to " << mValue << " : " << mOffset; + if (mIsNative) { - if (bInteger) - iOffset = -SYSTEM_CURRENCY_PRECISION; + if (smMatch[3].matched) + mOffset -= SYSTEM_CURRENCY_PRECISION; - while (iOffset > -SYSTEM_CURRENCY_PRECISION) { - uValue *= 10; - --iOffset; + while (mOffset > 0) + { + mValue *= 10; + --mOffset; } - while (iOffset < -SYSTEM_CURRENCY_PRECISION) { - uValue /= 10; - ++iOffset; + while (mOffset < 0) { + mValue /= 10; + ++mOffset; } - - mValue = uValue; } else - { - mValue = uValue; - mOffset = iOffset; canonicalize(); - } + return true; } @@ -1287,11 +1231,13 @@ BOOST_AUTO_TEST_CASE( setValue_test ) { STAmount saTmp; +#if 0 // Check native floats saTmp.setFullValue("1^0"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS == saTmp.getNValue(), "float integer failed"); saTmp.setFullValue("0^1"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS/10 == saTmp.getNValue(), "float fraction failed"); saTmp.setFullValue("0^12"); BOOST_CHECK_MESSAGE(12*SYSTEM_CURRENCY_PARTS/100 == saTmp.getNValue(), "float fraction failed"); saTmp.setFullValue("1^2"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS+(2*SYSTEM_CURRENCY_PARTS/10) == saTmp.getNValue(), "float combined failed"); +#endif // Check native integer saTmp.setFullValue("1"); BOOST_CHECK_MESSAGE(1 == saTmp.getNValue(), "integer failed"); diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index e43b959184..e7961cc33a 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -48,7 +48,8 @@ DatabaseCon::~DatabaseCon() Application::Application() : mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster), mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120), - mSNTPClient(mAuxService), mFeeTrack(), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), + mSNTPClient(mAuxService), mJobQueue(mIOService), mFeeTrack(), + mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), mPathFindDB(NULL), mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), mSweepTimer(mAuxService), mShutdown(false) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 662170f2a8..6224869405 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -134,72 +134,70 @@ Json::Value RPCParser::parseInternal(const Json::Value& jvParams) return v; } -// account_info || -// account_info || [[] ] -Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams) -{ - Json::Value jvRequest(Json::objectValue); - std::string strIdent = jvParams[0u].asString(); - // YYY This could be more strict and report casting errors. - int iIndex = 2 == jvParams.size() ? lexical_cast_s(jvParams[1u].asString()) : 0; - - RippleAddress raAddress; - - if (!raAddress.setAccountPublic(strIdent) && !raAddress.setAccountID(strIdent) && !raAddress.setSeedGeneric(strIdent)) - return rpcError(rpcACT_MALFORMED); - - jvRequest["ident"] = strIdent; - jvRequest["account_index"] = iIndex; - - if (jvParams.size() == 3 && !jvParseLedger(jvRequest, jvParams[2u].asString())) - return rpcError(rpcLGR_IDX_MALFORMED); - - return jvRequest; -} - -// account_tx -// account_tx -// account_tx binary -// account_tx binary +// account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending] Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) { Json::Value jvRequest(Json::objectValue); RippleAddress raAccount; - - unsigned size = jvParams.size(); - - if ((size > 1) && (jvParams[size - 1].asString() == "binary")) - { - jvRequest["binary"] = true; - --size; - } - - if (size < 2 || size > 3) - return rpcError(rpcINVALID_PARAMS); + unsigned int iParams = jvParams.size(); if (!raAccount.setAccountID(jvParams[0u].asString())) return rpcError(rpcACT_MALFORMED); - // YYY This could be more strict and report casting errors. - if (size == 2) + jvRequest["account"] = raAccount.humanAccountID(); + + bool bDone = false; + + while (!bDone && iParams >= 2) { + if (jvParams[iParams-1].asString() == "binary") + { + jvRequest["binary"] = true; + --iParams; + } + else if (jvParams[iParams-1].asString() == "count") + { + jvRequest["count"] = true; + --iParams; + } + else if (jvParams[iParams-1].asString() == "descending") + { + jvRequest["descending"] = true; + --iParams; + } + else + { + bDone = true; + } + } + + if (1 == iParams) { - jvRequest["ledger"] = jvParams[1u].asUInt(); + nothing(); + } + else if (2 == iParams) + { + if (!jvParseLedger(jvRequest, jvParams[1u].asString())) + return jvRequest; } else { - uint32 uLedgerMin = jvParams[1u].asUInt(); - uint32 uLedgerMax = jvParams[2u].asUInt(); + int64 uLedgerMin = jvParams[1u].asInt(); + int64 uLedgerMax = jvParams[2u].asInt(); - if ((uLedgerMax < uLedgerMin) || (uLedgerMax == 0)) + if (uLedgerMax != -1 && uLedgerMax < uLedgerMin) { return rpcError(rpcLGR_IDXS_INVALID); } - jvRequest["ledger_min"] = uLedgerMin; - jvRequest["ledger_max"] = uLedgerMax; - } + jvRequest["ledger_index_min"] = jvParams[1u].asInt(); + jvRequest["ledger_index_max"] = jvParams[2u].asInt(); - jvRequest["account"] = raAccount.humanAccountID(); + if (iParams >= 4) + jvRequest["limit"] = jvParams[3u].asInt(); + + if (iParams >= 5) + jvRequest["offset"] = jvParams[4u].asInt(); + } return jvRequest; } @@ -420,30 +418,34 @@ Json::Value RPCParser::parseLogLevel(const Json::Value& jvParams) } // owner_info || -// owner_info || [] -Json::Value RPCParser::parseOwnerInfo(const Json::Value& jvParams) -{ - return parseAccountInfo(jvParams); -} - -// account_lines || [] -// account_offers || [] +// owner_info || [] +// account_info || +// account_info || [] +// account_lines || [] +// account_offers || [] Json::Value RPCParser::parseAccountItems(const Json::Value& jvParams) { std::string strIdent = jvParams[0u].asString(); - bool bIndex = 2 == jvParams.size(); - int iIndex = bIndex ? lexical_cast_s(jvParams[1u].asString()) : 0; + // TODO: Get index from an alternate syntax: rXYZ: + int iIndex = 0; +// int iIndex = jvParams.size() >= 2 ? lexical_cast_s(jvParams[1u].asString()) : 0; - if (bIndex && !iIndex) // Don't send default. - bIndex = false; + RippleAddress raAddress; + + if (!raAddress.setAccountPublic(strIdent) && !raAddress.setAccountID(strIdent) && !raAddress.setSeedGeneric(strIdent)) + return rpcError(rpcACT_MALFORMED); // Get info on account. Json::Value jvRequest(Json::objectValue); jvRequest["account"] = strIdent; - if (bIndex) + + if (iIndex) jvRequest["account_index"] = iIndex; + if (jvParams.size() == 2 && !jvParseLedger(jvRequest, jvParams[1u].asString())) + return rpcError(rpcLGR_IDX_MALFORMED); + return jvRequest; } @@ -456,13 +458,13 @@ Json::Value RPCParser::parseRipplePathFind(const Json::Value& jvParams) cLog(lsTRACE) << "RPC json: " << jvParams[0u]; - if (bLedger) - { - jvParseLedger(jvRequest, jvParams[1u].asString()); - } - if (reader.parse(jvParams[0u].asString(), jvRequest)) { + if (bLedger) + { + jvParseLedger(jvRequest, jvParams[1u].asString()); + } + return jvRequest; } @@ -642,10 +644,10 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) // Request-response methods // - Returns an error, or the request. // - To modify the method, provide a new method in the request. - { "account_info", &RPCParser::parseAccountInfo, 1, 3 }, + { "account_info", &RPCParser::parseAccountItems, 1, 2 }, { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, - { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, + { "account_tx", &RPCParser::parseAccountTransactions, 1, 8 }, { "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, @@ -660,7 +662,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "log_level", &RPCParser::parseLogLevel, 0, 2 }, { "logrotate", &RPCParser::parseAsIs, 0, 0 }, // { "nickname_info", &RPCParser::parseNicknameInfo, 1, 1 }, - { "owner_info", &RPCParser::parseOwnerInfo, 1, 2 }, + { "owner_info", &RPCParser::parseAccountItems, 1, 2 }, { "peers", &RPCParser::parseAsIs, 0, 0 }, { "ping", &RPCParser::parseAsIs, 0, 0 }, // { "profile", &RPCParser::parseProfile, 1, 9 }, diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index c87ffbe958..4bc3e8439d 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -10,7 +10,6 @@ class RPCParser protected: typedef Json::Value (RPCParser::*parseFuncPtr)(const Json::Value &jvParams); - Json::Value parseAccountInfo(const Json::Value& jvParams); Json::Value parseAccountItems(const Json::Value& jvParams); Json::Value parseAccountTransactions(const Json::Value& jvParams); Json::Value parseAsIs(const Json::Value& jvParams); diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 096683d3f7..eb904eb594 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -306,7 +306,7 @@ void Config::load() smtTmp = sectionEntries(secConfig, SECTION_RPC_STARTUP); if (smtTmp) { - Json::Value jvArray(Json::arrayValue); + RPC_STARTUP = Json::arrayValue; BOOST_FOREACH(const std::string& strJson, *smtTmp) { @@ -316,10 +316,8 @@ void Config::load() if (!jrReader.parse(strJson, jvCommand)) throw std::runtime_error(boost::str(boost::format("Couldn't parse [" SECTION_RPC_STARTUP "] command: %s") % strJson)); - jvArray.append(jvCommand); + RPC_STARTUP.append(jvCommand); } - - RPC_STARTUP = jvArray; } if (sectionSingleB(secConfig, SECTION_DATABASE_PATH, DATABASE_PATH)) @@ -498,7 +496,7 @@ int Config::getSize(SizedItemName item) { siLineCacheSize, { 8192, 32768, 131072, 1048576, 0 } }, { siLineCacheAge, { 500, 600, 1800, 3600, 7200 } }, { siHashNodeDBCache, { 24, 48, 64, 128, 256 } }, - { siTxnDBCache, { 4, 8, 32, 64, 128 } }, + { siTxnDBCache, { 4, 12, 48, 96, 192 } }, { siLgrDBCache, { 4, 8, 32, 64, 128 } } }; diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp index 082ccd5677..60210514aa 100644 --- a/src/cpp/ripple/DBInit.cpp +++ b/src/cpp/ripple/DBInit.cpp @@ -19,6 +19,9 @@ const char *TxnDBInit[] = { RawTxn BLOB, \ TxnMeta BLOB \ );", + "CREATE INDEX TxLgrIndex ON \ + Transactions(LedgerSeq);", + "CREATE TABLE AccountTransactions ( \ TransID CHARACTER(64), \ Account CHARACTER(64), \ diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 7e97602a8c..0725d5530f 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -183,8 +183,8 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) #ifndef NO_SQLITE3_PREPARE { - LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve")); ScopedLock sl(theApp->getHashNodeDB()->getDBLock()); + LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve")); static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;"); diff --git a/src/cpp/ripple/InstanceCounter.h b/src/cpp/ripple/InstanceCounter.h index 95301cbbfa..5a8fe1c612 100644 --- a/src/cpp/ripple/InstanceCounter.h +++ b/src/cpp/ripple/InstanceCounter.h @@ -50,6 +50,11 @@ public: sMultiThreaded = true; } + static void shutdown() + { + sMultiThreaded = false; + } + static bool isMultiThread() { return sMultiThreaded; diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index 303035fe44..d169a9ddad 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -6,10 +6,12 @@ #include "Log.h" #include "Config.h" +#include "Application.h" SETUP_LOG(); -JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false) +JobQueue::JobQueue(boost::asio::io_service& svc) + : mLastJob(0), mThreadCount(0), mShuttingDown(false), mIOThreadCount(0), mMaxIOThreadCount(1), mIOService(svc) { mJobLoads[jtPUBOLDLEDGER].setTargetLatency(10000, 15000); mJobLoads[jtVALIDATION_ut].setTargetLatency(2000, 5000); @@ -240,6 +242,8 @@ void JobQueue::setThreadCount(int c) boost::mutex::scoped_lock sl(mJobLock); + mMaxIOThreadCount = 1 + (c / 3); + while (mJobCounts[jtDEATH].first != 0) mJobCond.wait(sl); @@ -261,6 +265,26 @@ void JobQueue::setThreadCount(int c) mJobCond.notify_one(); // in case we sucked up someone else's signal } +void JobQueue::IOThread(boost::mutex::scoped_lock& sl) +{ // call with a lock + ++mIOThreadCount; + sl.unlock(); + NameThread("IO+"); + try + { + do + NameThread("IO+"); + while ((mIOService.poll_one() == 1) && !theApp->isShutdown()); + } + catch (...) + { + cLog(lsWARNING) << "Exception in IOThread"; + } + NameThread("waiting"); + sl.lock(); + --mIOThreadCount; +} + void JobQueue::threadEntry() { // do jobs until asked to stop boost::mutex::scoped_lock sl(mJobLock); @@ -268,7 +292,12 @@ void JobQueue::threadEntry() { NameThread("waiting"); while (mJobSet.empty() && !mShuttingDown) - mJobCond.wait(sl); + { + if ((mIOThreadCount < mMaxIOThreadCount) && !theApp->isShutdown()) + IOThread(sl); + else + mJobCond.wait(sl); + } if (mShuttingDown) break; diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h index 705744db66..62e058c009 100644 --- a/src/cpp/ripple/JobQueue.h +++ b/src/cpp/ripple/JobQueue.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "../json/value.h" @@ -93,14 +94,19 @@ protected: int mThreadCount; bool mShuttingDown; + int mIOThreadCount; + int mMaxIOThreadCount; + boost::asio::io_service& mIOService; + std::map > mJobCounts; - void threadEntry(void); + void threadEntry(); + void IOThread(boost::mutex::scoped_lock&); public: - JobQueue(); + JobQueue(boost::asio::io_service&); void addJob(JobType type, const std::string& name, const FUNCTION_TYPE& job); diff --git a/src/cpp/ripple/LedgerAcquire.cpp b/src/cpp/ripple/LedgerAcquire.cpp index e345dc18ca..30b61e0af5 100644 --- a/src/cpp/ripple/LedgerAcquire.cpp +++ b/src/cpp/ripple/LedgerAcquire.cpp @@ -13,9 +13,9 @@ SETUP_LOG(); DECLARE_INSTANCE(LedgerAcquire); #define LA_DEBUG -#define LEDGER_ACQUIRE_TIMEOUT 1000 // millisecond for each ledger timeout +#define LEDGER_ACQUIRE_TIMEOUT 2000 // millisecond for each ledger timeout #define LEDGER_TIMEOUT_COUNT 10 // how many timeouts before we giveup -#define LEDGER_TIMEOUT_AGGRESSIVE 4 // how many timeouts before we get aggressive +#define LEDGER_TIMEOUT_AGGRESSIVE 6 // how many timeouts before we get aggressive #define TRUST_NETWORK PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0), @@ -98,7 +98,7 @@ bool PeerSet::isActive() LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT), mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false), - mByHash(true) + mByHash(true), mWaitCount(0) { #ifdef LA_DEBUG cLog(lsTRACE) << "Acquiring ledger " << mHash; @@ -191,6 +191,9 @@ bool LedgerAcquire::tryLocal() void LedgerAcquire::onTimer(bool progress) { + mRecentTXNodes.clear(); + mRecentASNodes.clear(); + if (getTimeouts() > LEDGER_TIMEOUT_COUNT) { cLog(lsWARNING) << "Too many timeouts for ledger " << mHash; @@ -199,21 +202,31 @@ void LedgerAcquire::onTimer(bool progress) return; } - mRecentTXNodes.clear(); - mRecentASNodes.clear(); - if (!progress) { mAggressive = true; mByHash = true; - cLog(lsDEBUG) << "No progress for ledger " << mHash; - if (!getPeerCount()) + int pc = getPeerCount(); + cLog(lsDEBUG) << "No progress(" << pc << ") for ledger " << pc << mHash; + if (pc == 0) addPeers(); else trigger(Peer::pointer()); } } +void LedgerAcquire::awaitData() +{ + boost::recursive_mutex::scoped_lock sl(mLock); + ++mWaitCount; +} + +void LedgerAcquire::noAwaitData() +{ + boost::recursive_mutex::scoped_lock sl(mLock); + if (mWaitCount > 0 ) --mWaitCount; +} + void LedgerAcquire::addPeers() { std::vector peerList = theApp->getConnectionPool().getPeerVector(); @@ -295,8 +308,16 @@ void LedgerAcquire::trigger(Peer::ref peer) boost::recursive_mutex::scoped_lock sl(mLock); if (mAborted || mComplete || mFailed) { - cLog(lsTRACE) << "Trigger on ledger:" << - (mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : ""); + cLog(lsDEBUG) << "Trigger on ledger:" << + (mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : "") << + " wc=" << mWaitCount; + return; + } + + if ((mWaitCount > 0) && peer) + { + mRecentPeers.push_back(peer->getPeerId()); + cLog(lsTRACE) << "Deferring peer"; return; } @@ -484,6 +505,8 @@ void LedgerAcquire::trigger(Peer::ref peer) } } + mRecentPeers.clear(); + if (mComplete || mFailed) { cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed " : " ") @@ -786,13 +809,13 @@ std::vector LedgerAcquire::getNeededHashes() } if (!mHaveState) { - std::vector v = mLedger->getNeededAccountStateHashes(16); + std::vector v = mLedger->getNeededAccountStateHashes(4); BOOST_FOREACH(const uint256& h, v) ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otSTATE_NODE, h)); } if (!mHaveTransactions) { - std::vector v = mLedger->getNeededAccountStateHashes(16); + std::vector v = mLedger->getNeededAccountStateHashes(4); BOOST_FOREACH(const uint256& h, v) ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otTRANSACTION_NODE, h)); } @@ -846,21 +869,21 @@ void LedgerAcquireMaster::dropLedger(const uint256& hash) mLedgers.erase(hash); } -void LedgerAcquireMaster::gotLedgerData(Job&, boost::shared_ptr packet_ptr, - boost::weak_ptr wPeer) +bool LedgerAcquireMaster::awaitLedgerData(const uint256& ledgerHash) +{ + LedgerAcquire::pointer ledger = find(ledgerHash); + if (!ledger) + return false; + ledger->awaitData(); + return true; +} + +void LedgerAcquireMaster::gotLedgerData(Job&, uint256 hash, + boost::shared_ptr packet_ptr, boost::weak_ptr wPeer) { ripple::TMLedgerData& packet = *packet_ptr; Peer::pointer peer = wPeer.lock(); - if (!peer) - return; - uint256 hash; - if (packet.ledgerhash().size() != 32) - { - peer->punishPeer(LT_InvalidRequest); - return; - } - memcpy(hash.begin(), packet.ledgerhash().data(), 32); cLog(lsTRACE) << "Got data (" << packet.nodes().size() << ") for acquiring ledger: " << hash; LedgerAcquire::pointer ledger = find(hash); @@ -870,6 +893,10 @@ void LedgerAcquireMaster::gotLedgerData(Job&, boost::shared_ptrpunishPeer(LT_InvalidRequest); return; } + ledger->noAwaitData(); + + if (!peer) + return; if (packet.type() == ripple::liBASE) { diff --git a/src/cpp/ripple/LedgerAcquire.h b/src/cpp/ripple/LedgerAcquire.h index c255ef6b7e..4193ce96e1 100644 --- a/src/cpp/ripple/LedgerAcquire.h +++ b/src/cpp/ripple/LedgerAcquire.h @@ -87,11 +87,14 @@ public: protected: Ledger::pointer mLedger; bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept, mByHash; + int mWaitCount; std::set mRecentTXNodes; std::set mRecentASNodes; - std::vector< FUNCTION_TYPE > mOnComplete; + std::vector mRecentPeers; + + std::vector< FUNCTION_TYPE > mOnComplete; void done(); void onTimer(bool progress); @@ -124,6 +127,8 @@ public: void trigger(Peer::ref); bool tryLocal(); void addPeers(); + void awaitData(); + void noAwaitData(); typedef std::pair neededHash_t; std::vector getNeededHashes(); @@ -148,7 +153,9 @@ public: LedgerAcquire::pointer find(const uint256& hash); bool hasLedger(const uint256& ledgerHash); void dropLedger(const uint256& ledgerHash); - void gotLedgerData(Job&, boost::shared_ptr packet, boost::weak_ptr peer); + + bool awaitLedgerData(const uint256& ledgerHash); + void gotLedgerData(Job&, uint256 hash, boost::shared_ptr packet, boost::weak_ptr peer); int getFetchCount(int& timeoutCount); void logFailure(const uint256& h) { mRecentFailures.add(h); } diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp index cdf9d30f30..d96a820fda 100644 --- a/src/cpp/ripple/LedgerEntrySet.cpp +++ b/src/cpp/ripple/LedgerEntrySet.cpp @@ -230,6 +230,15 @@ void LedgerEntrySet::entryDelete(SLE::ref sle) } } +bool LedgerEntrySet::hasChanges() +{ + typedef std::map::value_type u256_LES_pair; + BOOST_FOREACH(u256_LES_pair& it, mEntries) + if (it.second.mAction != taaCACHED) + return true; + return false; +} + bool LedgerEntrySet::intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight) { return true; // XXX Needs implementation diff --git a/src/cpp/ripple/LedgerEntrySet.h b/src/cpp/ripple/LedgerEntrySet.h index c326cc5389..09f9bb0cee 100644 --- a/src/cpp/ripple/LedgerEntrySet.h +++ b/src/cpp/ripple/LedgerEntrySet.h @@ -95,6 +95,7 @@ public: void entryCreate(SLE::ref); // This entry will be created void entryDelete(SLE::ref); // This entry will be deleted void entryModify(SLE::ref); // This entry will be modified + bool hasChanges(); // True if LES has any changes // higher-level ledger functions SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex); diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index 4d4920f746..67381e195e 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -141,6 +141,22 @@ bool LedgerMaster::haveLedger(uint32 seq) return mCompleteLedgers.hasValue(seq); } +bool LedgerMaster::getValidatedRange(uint32& minVal, uint32& maxVal) +{ + boost::recursive_mutex::scoped_lock sl(mLock); + if (!mValidLedger) + return false; + maxVal = mValidLedger->getLedgerSeq(); + if (maxVal == 0) + return false; + minVal = mCompleteLedgers.prevMissing(maxVal); + if (minVal == RangeSet::RangeSetAbsent) + minVal = 0; + else + ++minVal; + return true; +} + void LedgerMaster::asyncAccept(Ledger::pointer ledger) { uint32 seq = ledger->getLedgerSeq(); @@ -232,7 +248,7 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l int timeoutCount; int fetchCount = theApp->getMasterLedgerAcquire().getFetchCount(timeoutCount); - if (fetchCount < fetchMax) + if ((fetchCount < fetchMax) && theApp->getOPs().isFull()) { if (timeoutCount > 2) { @@ -289,7 +305,7 @@ bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uin void LedgerMaster::resumeAcquiring() { - if (theApp->getOPs().isNeedNetworkLedger()) + if (!theApp->getOPs().isFull()) return; boost::recursive_mutex::scoped_lock ml(mLock); diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h index d2dc0c2d57..6c9d87122b 100644 --- a/src/cpp/ripple/LedgerMaster.h +++ b/src/cpp/ripple/LedgerMaster.h @@ -143,6 +143,7 @@ public: bool haveLedgerRange(uint32 from, uint32 to); bool haveLedger(uint32 seq); + bool getValidatedRange(uint32& minVal, uint32& maxVal); void resumeAcquiring(); diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 7b46154400..8b3e8aa98c 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -564,7 +564,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre // void NetworkOPs::setStateTimer() -{ // set timer early if ledger is closing +{ mNetTimer.expires_from_now(boost::posix_time::milliseconds(LEDGER_GRANULARITY)); mNetTimer.async_wait(boost::bind(&NetworkOPs::checkState, this, boost::asio::placeholders::error)); } @@ -594,36 +594,41 @@ void NetworkOPs::checkState(const boost::system::error_code& result) { // Network state machine if ((result == boost::asio::error::operation_aborted) || theConfig.RUN_STANDALONE) + { + cLog(lsFATAL) << "Network state timer error: " << result; return; + } + + { + ScopedLock(theApp->getMasterLock()); + + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + + // do we have sufficient peers? If not, we are disconnected. + if (peerList.size() < theConfig.NETWORK_QUORUM) + { + if (mMode != omDISCONNECTED) + { + setMode(omDISCONNECTED); + cLog(lsWARNING) << "Node count (" << peerList.size() << + ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ")."; + } + return; + } + if (mMode == omDISCONNECTED) + { + setMode(omCONNECTED); + cLog(lsINFO) << "Node count (" << peerList.size() << ") is sufficient."; + } + + if (!mConsensus) + tryStartConsensus(); + + if (mConsensus) + mConsensus->timerEntry(); + } setStateTimer(); - - ScopedLock(theApp->getMasterLock()); - - std::vector peerList = theApp->getConnectionPool().getPeerVector(); - - // do we have sufficient peers? If not, we are disconnected. - if (peerList.size() < theConfig.NETWORK_QUORUM) - { - if (mMode != omDISCONNECTED) - { - setMode(omDISCONNECTED); - cLog(lsWARNING) << "Node count (" << peerList.size() << - ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ")."; - } - return; - } - if (mMode == omDISCONNECTED) - { - setMode(omCONNECTED); - cLog(lsINFO) << "Node count (" << peerList.size() << ") is sufficient."; - } - - if (!mConsensus) - tryStartConsensus(); - - if (mConsensus) - mConsensus->timerEntry(); } void NetworkOPs::tryStartConsensus() @@ -1059,17 +1064,56 @@ void NetworkOPs::setMode(OperatingMode om) } +std::string + NetworkOPs::transactionsSQL(std::string selection, const RippleAddress& account, + int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, + bool binary, bool count, bool bAdmin) +{ + uint32 NONBINARY_PAGE_LENGTH = 200; + uint32 BINARY_PAGE_LENGTH = 500; + + uint32 numberOfResults; + if (count) + numberOfResults = 1000000000; + else if (limit < 0) + numberOfResults = binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH; + else if (!bAdmin) + numberOfResults = std::min(binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH, static_cast(limit)); + else + numberOfResults = limit; + + std::string maxClause = ""; + std::string minClause = ""; + if (maxLedger != -1) + maxClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % maxLedger); + if (minLedger != -1) + minClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % minLedger); + + std::string sql = + boost::str(boost::format("SELECT %s FROM " + "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " + "WHERE Account = '%s' %s %s " + "ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TransID %s LIMIT %u, %u;") + % selection + % account.humanAccountID() + % maxClause + % minClause + % (descending ? "DESC" : "ASC") + % (descending ? "DESC" : "ASC") + % boost::lexical_cast(offset) + % boost::lexical_cast(numberOfResults) + ); + cLog(lsTRACE) << "txSQL query: " << sql; + return sql; +} + std::vector< std::pair > - NetworkOPs::getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger) + NetworkOPs::getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin) { // can be called with no locks std::vector< std::pair > ret; - std::string sql = - str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " - "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " - "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " - "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC LIMIT 200;") - % account.humanAccountID() % maxLedger % minLedger); + std::string sql = NetworkOPs::transactionsSQL("AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account, + minLedger, maxLedger, descending, offset, limit, false, false, bAdmin); { Database* db = theApp->getTxnDB()->getDB(); @@ -1098,15 +1142,12 @@ std::vector< std::pair > } std::vector NetworkOPs::getAccountTxsB( - const RippleAddress& account, uint32 minLedger, uint32 maxLedger) + const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin) { // can be called with no locks std::vector< txnMetaLedgerType> ret; - std::string sql = str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " - "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " - "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " - "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC LIMIT 500;") - % account.humanAccountID() % maxLedger % minLedger); + std::string sql = NetworkOPs::transactionsSQL("AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account, + minLedger, maxLedger, descending, offset, limit, true/*binary*/, false, bAdmin); { Database* db = theApp->getTxnDB()->getDB(); @@ -1144,6 +1185,26 @@ std::vector NetworkOPs::getAccountTxsB( } + + +uint32 + NetworkOPs::countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger) +{ // can be called with no locks + uint32 ret = 0; + std::string sql = NetworkOPs::transactionsSQL("COUNT(*) AS 'TransactionCount'", account, + minLedger, maxLedger, false, 0, -1, true, true, true); + + Database* db = theApp->getTxnDB()->getDB(); + ScopedLock sl(theApp->getTxnDB()->getDBLock()); + SQL_FOREACH(db, sql) + { + ret = db->getInt("TransactionCount"); + } + + return ret; +} + + std::vector NetworkOPs::getLedgerAffectedAccounts(uint32 ledgerSeq) { diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 00cb710c92..ee656a0612 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -175,6 +175,7 @@ public: bool isValidated(uint32 seq); bool isValidated(uint32 seq, const uint256& hash); bool isValidated(Ledger::ref l) { return isValidated(l->getLedgerSeq(), l->getHash()); } + bool getValidatedRange(uint32& minVal, uint32& maxVal) { return mLedgerMaster->getValidatedRange(minVal, maxVal); } SerializedValidation::ref getLastValidation() { return mLastValidation; } void setLastValidation(SerializedValidation::ref v) { mLastValidation = v; } @@ -272,6 +273,7 @@ public: void needNetworkLedger() { mNeedNetworkLedger = true; } void clearNeedNetworkLedger() { mNeedNetworkLedger = false; } bool isNeedNetworkLedger() { return mNeedNetworkLedger; } + bool isFull() { return !mNeedNetworkLedger && (mMode == omFULL); } void setProposing(bool p, bool v) { mProposing = p; mValidating = v; } bool isProposing() { return mProposing; } bool isValidating() { return mValidating; } @@ -292,17 +294,23 @@ public: bool addWantedHash(const uint256& h); bool isWantedHash(const uint256& h, bool remove); + //Helper function to generate SQL query to get transactions + std::string transactionsSQL(std::string selection, const RippleAddress& account, + int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, + bool binary, bool count, bool bAdmin); + + // client information retrieval functions std::vector< std::pair > - getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger); + getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin); typedef boost::tuple txnMetaLedgerType; std::vector - getAccountTxsB(const RippleAddress& account, uint32 minL, uint32 maxL); + getAccountTxsB(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin); std::vector getLedgerAffectedAccounts(uint32 ledgerSeq); std::vector getLedgerTransactions(uint32 ledgerSeq); - + uint32 countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger); // // Monitoring: publisher side // diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index ee29d50fe2..c1438825f1 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -119,6 +119,10 @@ bool Pathfinder::bDefaultPath(const STPath& spPath) bool bDefault; LedgerEntrySet lesActive(mLedger, tapNONE); + cLog(lsTRACE) << boost::str(boost::format("bDefaultPath> mSrcAmount=%s mDstAmount=%s") + % mSrcAmount.getFullText() + % mDstAmount.getFullText()); + // Expand the current path. pspCurrent->setExpanded(lesActive, spPath, mDstAccountID, mSrcAccountID); @@ -126,8 +130,8 @@ bool Pathfinder::bDefaultPath(const STPath& spPath) // When path is a default (implied). Don't need to add it to return set. bDefault = pspCurrent->vpnNodes == mPsDefault->vpnNodes; - cLog(lsTRACE) << "findPaths: expanded path: " << pspCurrent->getJson(); - cLog(lsTRACE) << "findPaths: default path: indirect: " << spPath.getJson(0); + cLog(lsTRACE) << "bDefaultPath: expanded path: " << pspCurrent->getJson(); + cLog(lsTRACE) << "bDefaultPath: default path: indirect: " << spPath.getJson(0); return bDefault; } @@ -162,6 +166,10 @@ Pathfinder::Pathfinder(Ledger::ref ledger, LedgerEntrySet lesActive(mLedger, tapNONE); + cLog(lsTRACE) << boost::str(boost::format("Pathfinder> mSrcAmount=%s mDstAmount=%s") + % mSrcAmount.getFullText() + % mDstAmount.getFullText()); + psDefault->setExpanded(lesActive, STPath(), mDstAccountID, mSrcAccountID); if (tesSUCCESS == psDefault->terStatus) @@ -296,6 +304,12 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax if (sLog(lsTRACE)) { + cLog(lsTRACE) << boost::str(boost::format("findPaths: spe: %s/%s: %s amt: %s") + % RippleAddress::createHumanAccountID(speEnd.mAccountID) + % RippleAddress::createHumanAccountID(speEnd.mIssuerID) + % RippleAddress::createHumanAccountID(mDstAccountID) + % RippleAddress::createHumanAccountID(mDstAmount.getIssuer())); + cLog(lsTRACE) << "findPaths: finish? account: " << (speEnd.mAccountID == mDstAccountID); cLog(lsTRACE) << "findPaths: finish? currency: " << (speEnd.mCurrencyID == mDstAmount.getCurrency()); cLog(lsTRACE) << "findPaths: finish? issuer: " @@ -408,7 +422,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax SLE::pointer sleEnd = lesActive.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(speEnd.mAccountID)); tLog(!sleEnd, lsDEBUG) - << boost::str(boost::format("findPaths: order book: %s/%s : ") + << boost::str(boost::format("findPaths: tail: %s/%s : ") % RippleAddress::createHumanAccountID(speEnd.mAccountID) % RippleAddress::createHumanAccountID(speEnd.mIssuerID)); @@ -445,11 +459,14 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax { // Path has no credit left. Ignore it. cLog(lsTRACE) << - boost::str(boost::format("findPaths: No credit: %s/%s -> %s/%s") + boost::str(boost::format("findPaths: No credit: %s/%s -> %s/%s balance=%s limit=%s") % RippleAddress::createHumanAccountID(speEnd.mAccountID) % STAmount::createHumanCurrency(speEnd.mCurrencyID) % RippleAddress::createHumanAccountID(uPeerID) - % STAmount::createHumanCurrency(speEnd.mCurrencyID)); + % STAmount::createHumanCurrency(speEnd.mCurrencyID) + % rspEntry->getBalance().getFullText() + % rspEntry->getLimitPeer().getFullText() + ); } else { @@ -463,19 +480,23 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax bContinued = true; cLog(lsTRACE) << - boost::str(boost::format("findPaths: push explore: %s/%s -> %s/%s") + boost::str(boost::format("findPaths: push explore: %s/%s -> %s/%s balance=%s limit=%s limit_peer=%s") % STAmount::createHumanCurrency(speEnd.mCurrencyID) % RippleAddress::createHumanAccountID(speEnd.mAccountID) % STAmount::createHumanCurrency(speEnd.mCurrencyID) - % RippleAddress::createHumanAccountID(uPeerID)); + % RippleAddress::createHumanAccountID(uPeerID) + % rspEntry->getBalance().getFullText() + % rspEntry->getLimit().getFullText() + % rspEntry->getLimitPeer().getFullText()); } } } - // XXX Flip argument order to norm. + // XXX Flip argument order to norm. (currency, issuer) std::vector books; theApp->getOrderBookDB().getBooksByTakerPays(speEnd.mIssuerID, speEnd.mCurrencyID, books); + BOOST_FOREACH(OrderBook::ref book, books) { if (!spPath.hasSeen(ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut())) @@ -533,8 +554,10 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax TER terResult; try { + LedgerEntrySet lesSandbox(lesActive.duplicate()); + terResult = RippleCalc::rippleCalc( - lesActive, + lesSandbox, saMaxAmountAct, saDstAmountAct, vpsExpanded, diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp index 4a87a4dc4d..55bb4f4e09 100644 --- a/src/cpp/ripple/Peer.cpp +++ b/src/cpp/ripple/Peer.cpp @@ -1659,18 +1659,17 @@ void Peer::recvLedger(const boost::shared_ptr& packet_ptr) return; } + uint256 hash; + if(packet.ledgerhash().size() != 32) + { + cLog(lsWARNING) << "TX candidate reply with invalid hash size"; + punishPeer(LT_InvalidRequest); + return; + } + memcpy(hash.begin(), packet.ledgerhash().data(), 32); + if (packet.type() == ripple::liTS_CANDIDATE) { // got data for a candidate transaction set - uint256 hash; - if(packet.ledgerhash().size() != 32) - { - cLog(lsWARNING) << "TX candidate reply with invalid hash size"; - punishPeer(LT_InvalidRequest); - return; - } - memcpy(hash.begin(), packet.ledgerhash().data(), 32); - - std::list nodeIDs; std::list< std::vector > nodeData; @@ -1692,9 +1691,12 @@ void Peer::recvLedger(const boost::shared_ptr& packet_ptr) return; } - theApp->getJobQueue().addJob(jtLEDGER_DATA, "gotLedgerData", - BIND_TYPE(&LedgerAcquireMaster::gotLedgerData, &theApp->getMasterLedgerAcquire(), - P_1, packet_ptr, boost::weak_ptr(shared_from_this()))); + if (theApp->getMasterLedgerAcquire().awaitLedgerData(hash)) + theApp->getJobQueue().addJob(jtLEDGER_DATA, "gotLedgerData", + BIND_TYPE(&LedgerAcquireMaster::gotLedgerData, &theApp->getMasterLedgerAcquire(), + P_1, hash, packet_ptr, boost::weak_ptr(shared_from_this()))); + else + punishPeer(LT_UnwantedData); } bool Peer::hasLedger(const uint256& hash) const diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index cc0a84f7ee..dc798aefa3 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -9,7 +9,6 @@ #include "Pathfinder.h" #include "Log.h" -#include "NetworkOPs.h" #include "RPCHandler.h" #include "RPCSub.h" #include "Application.h" @@ -1235,7 +1234,6 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) cost = rpcCOST_EXPENSIVE; Ledger::pointer lSnapShot = boost::make_shared(boost::ref(*lpLedger), false); - LedgerEntrySet lesSnapshot(lSnapShot, tapNONE); ScopedUnlock su(theApp->getMasterLock()); // As long as we have a locked copy of the ledger, we can unlock. @@ -1243,6 +1241,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) for (unsigned int i=0; i != jvSrcCurrencies.size(); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; + uint160 uSrcCurrencyID; uint160 uSrcIssuerID; @@ -1277,7 +1276,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) if (!pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsComputed)) { - cLog(lsDEBUG) << "ripple_path_find: No paths found."; + cLog(lsWARNING) << "ripple_path_find: No paths found."; } else { @@ -1294,9 +1293,11 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) 1); saMaxAmount.negate(); + LedgerEntrySet lesSandbox(lSnapShot, tapNONE); + TER terResult = RippleCalc::rippleCalc( - lesSnapshot, + lesSandbox, saMaxAmountAct, // <-- saDstAmountAct, // <-- vpsExpanded, // <-- @@ -1314,8 +1315,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) // cLog(lsDEBUG) << "ripple_path_find: PATHS IN: " << spsComputed.size() << " : " << spsComputed.getJson(0); // cLog(lsDEBUG) << "ripple_path_find: PATHS EXP: " << vpsExpanded.size(); - - cLog(lsDEBUG) + cLog(lsWARNING) << boost::str(boost::format("ripple_path_find: saMaxAmount=%s saDstAmount=%s saMaxAmountAct=%s saDstAmountAct=%s") % saMaxAmount % saDstAmount @@ -1656,17 +1656,29 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest, int& cost) return ret; } -// { account: , ledger: } -// { account: , ledger_min: , ledger_max: } -// THIS ROUTINE DOESN'T SCALE. -// FIXME: Require admin. -// FIXME: Doesn't report database holes. -// FIXME: For consistency change inputs to: ledger_index, ledger_index_min, ledger_index_max. +// { +// account: account, +// ledger_index_min: ledger_index, +// ledger_index_max: ledger_index, +// binary: boolean, // optional, defaults to false +// count: boolean, // optional, defaults to false +// descending: boolean, // optional, defaults to false +// offset: integer, // optional, defaults to 0 +// limit: integer // optional +// } Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) { RippleAddress raAccount; - uint32 minLedger; - uint32 maxLedger; + uint32 offset = jvRequest.isMember("offset") ? jvRequest["offset"].asUInt() : 0; + int limit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : -1; + bool bBinary = jvRequest.isMember("binary") && jvRequest["binary"].asBool(); + bool bDescending = jvRequest.isMember("descending") && jvRequest["descending"].asBool(); + bool bCount = jvRequest.isMember("count") && jvRequest["count"].asBool(); + uint32 uLedgerMin; + uint32 uLedgerMax; + uint32 uValidatedMin; + uint32 uValidatedMax; + bool bValidated = mNetOps->getValidatedRange(uValidatedMin, uValidatedMax); if (!jvRequest.isMember("account")) return rpcError(rpcINVALID_PARAMS); @@ -1674,10 +1686,37 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) if (!raAccount.setAccountID(jvRequest["account"].asString())) return rpcError(rpcACT_MALFORMED); - if (jvRequest.isMember("ledger_min") && jvRequest.isMember("ledger_max")) + // DEPRECATED + if (jvRequest.isMember("ledger_min")) { - minLedger = jvRequest["ledger_min"].asUInt(); - maxLedger = jvRequest["ledger_max"].asUInt(); + jvRequest["ledger_index_min"] = jvRequest["ledger_min"]; + bDescending = true; + } + + // DEPRECATED + if (jvRequest.isMember("ledger_max")) + { + jvRequest["ledger_index_max"] = jvRequest["ledger_max"]; + bDescending = true; + } + + if (jvRequest.isMember("ledger_index_min") || jvRequest.isMember("ledger_index_max")) + { + int64 iLedgerMin = jvRequest.isMember("ledger_index_min") ? jvRequest["ledger_index_min"].asInt() : -1; + int64 iLedgerMax = jvRequest.isMember("ledger_index_max") ? jvRequest["ledger_index_max"].asInt() : -1; + + if (!bValidated && (iLedgerMin == -1 || iLedgerMax == -1)) { + // Don't have a validated ledger range. + return rpcError(rpcLGR_IDXS_INVALID); + } + + uLedgerMin = iLedgerMin == -1 ? uValidatedMin : iLedgerMin; + uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax; + + if (uLedgerMax < uLedgerMin) + { + return rpcError(rpcLGR_IDXS_INVALID); + } } else { @@ -1685,65 +1724,74 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) Json::Value ret = lookupLedger(jvRequest, l); if (!l) return ret; - minLedger = maxLedger = l->getLedgerSeq(); - } - - if ((maxLedger < minLedger) || (maxLedger == 0)) - { - return rpcError(rpcLGR_IDXS_INVALID); + uLedgerMin = uLedgerMax = l->getLedgerSeq(); } #ifndef DEBUG try { #endif - unsigned int vl = mNetOps->getValidatedSeq(); ScopedUnlock su(theApp->getMasterLock()); Json::Value ret(Json::objectValue); + ret["account"] = raAccount.humanAccountID(); - if (jvRequest.isMember("binary") && jvRequest["binary"].asBool()) + if (bBinary) { std::vector txns = - mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger); + mNetOps->getAccountTxsB(raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, mRole == ADMIN); + for (std::vector::const_iterator it = txns.begin(), end = txns.end(); it != end; ++it) { - Json::Value obj(Json::objectValue); - obj["transaction"] = it->get<0>(); - obj["meta"] = it->get<1>(); - obj["inLedger"] = it->get<2>(); - if (it->get<2>() > vl) - obj["validated"] = false; - else if (mNetOps->haveLedger(it->get<2>())) - obj["validated"] = true; - ret["transactions"].append(obj); + Json::Value jvObj(Json::objectValue); + uint32 uLedgerIndex = it->get<2>(); + + jvObj["tx_blob"] = it->get<0>(); + jvObj["meta"] = it->get<1>(); + jvObj["ledger_index"] = uLedgerIndex; + jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; + + ret["transactions"].append(jvObj); } } else { - std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger); + std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, mRole == ADMIN); + for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) { - Json::Value obj(Json::objectValue); + Json::Value jvObj(Json::objectValue); if (it->first) - obj["tx"] = it->first->getJson(1); + jvObj["tx"] = it->first->getJson(1); + if (it->second) { - obj["meta"] = it->second->getJson(0); + uint32 uLedgerIndex = it->second->getLgrSeq(); - uint32 s = it->second->getLgrSeq(); - if (s > vl) - obj["validated"] = false; - else if (mNetOps->haveLedger(s)) - obj["validated"] = true; + jvObj["meta"] = it->second->getJson(0); + jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } - ret["transactions"].append(obj); + ret["transactions"].append(jvObj); } } + + //Add information about the original query + ret["ledger_index_min"] = uLedgerMin; + ret["ledger_index_max"] = uLedgerMax; + ret["validated"] = bValidated && uValidatedMin <= uLedgerMin && uValidatedMax >= uLedgerMax; + ret["offset"] = offset; + + if (bCount) + ret["count"] = mNetOps->countAccountTxs(raAccount, uLedgerMin, uLedgerMax); + + if (jvRequest.isMember("limit")) + ret["limit"] = limit; + + return ret; #ifndef DEBUG } diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 63ba7b556b..b716ebc55f 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -8,6 +8,7 @@ #include "RippleAddress.h" #include "SerializedTypes.h" #include "Ledger.h" +#include "NetworkOPs.h" #define LEDGER_CURRENT -1 #define LEDGER_CLOSED -2 diff --git a/src/cpp/ripple/RippleAddress.cpp b/src/cpp/ripple/RippleAddress.cpp index aac1695e19..7ef204277e 100644 --- a/src/cpp/ripple/RippleAddress.cpp +++ b/src/cpp/ripple/RippleAddress.cpp @@ -18,9 +18,16 @@ #include "utils.h" #include "Log.h" #include "Serializer.h" +#include "Application.h" SETUP_LOG(); +std::size_t hash_value(const CBase58Data& b58) +{ + std::size_t seed = theApp->getNonceST() + (b58.nVersion * 0x9e3779b9); + boost::hash_combine(seed, b58.vchData); + return seed; +} RippleAddress::RippleAddress() { diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index bf94a47839..a8fe832076 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -250,9 +250,15 @@ TER PathState::pushNode( if (tesSUCCESS == terResult) { STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID); + STAmount saLimit; - if (!saOwed.isPositive() && -saOwed >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID)) + if (!saOwed.isPositive() + && -saOwed >= (saLimit = lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID))) { + cLog(lsWARNING) << boost::str(boost::format("pushNode: dry: saOwed=%s saLimit=%s") + % saOwed.getFullText() + % saLimit.getFullText()); + terResult = tecPATH_DRY; } } @@ -323,6 +329,8 @@ void PathState::setExpanded( const uint160 uOutIssuerID = saOutReq.getIssuer(); const uint160 uSenderIssuerID = !!uMaxCurrencyID ? uSenderID : ACCOUNT_XRP; // Sender is always issuer for non-XRP. + // cLog(lsDEBUG) << boost::str(boost::format("setExpanded>")); + lesEntries = lesSource.duplicate(); terStatus = tesSUCCESS; @@ -344,7 +352,7 @@ void PathState::setExpanded( uMaxCurrencyID, // Max specifes the currency. uSenderIssuerID); - cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s") + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: pushed: account=%s currency=%s issuer=%s") % RippleAddress::createHumanAccountID(uSenderID) % STAmount::createHumanCurrency(uMaxCurrencyID) % RippleAddress::createHumanAccountID(uSenderIssuerID)); @@ -366,8 +374,10 @@ void PathState::setExpanded( : uOutIssuerID // Use implied node. : ACCOUNT_XRP; - cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") - % RippleAddress::createHumanAccountID(uNxtCurrencyID) + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: implied check: uMaxIssuerID=%s uSenderIssuerID=%s uNxtCurrencyID=%s uNxtAccountID=%s") + % RippleAddress::createHumanAccountID(uMaxIssuerID) + % RippleAddress::createHumanAccountID(uSenderIssuerID) + % STAmount::createHumanCurrency(uNxtCurrencyID) % RippleAddress::createHumanAccountID(uNxtAccountID)); // Can't just use push implied, because it can't compensate for next account. @@ -375,9 +385,9 @@ void PathState::setExpanded( || uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next... || uMaxIssuerID != uNxtAccountID) // Next is not implied issuer { - cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s") + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: sender implied: account=%s currency=%s issuer=%s") % RippleAddress::createHumanAccountID(uMaxIssuerID) - % RippleAddress::createHumanAccountID(uMaxCurrencyID) + % STAmount::createHumanCurrency(uMaxCurrencyID) % RippleAddress::createHumanAccountID(uMaxIssuerID)); // Add account implied by SendMax. terStatus = pushNode( @@ -394,7 +404,7 @@ void PathState::setExpanded( { if (tesSUCCESS == terStatus) { - cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: element in path:")); terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID()); } } @@ -408,9 +418,9 @@ void PathState::setExpanded( || pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer. { // Add implied account. - cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s") + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: receiver implied: account=%s currency=%s issuer=%s") % RippleAddress::createHumanAccountID(uOutIssuerID) - % RippleAddress::createHumanAccountID(uOutCurrencyID) + % STAmount::createHumanCurrency(uOutCurrencyID) % RippleAddress::createHumanAccountID(uOutIssuerID)); terStatus = pushNode( !!uOutCurrencyID @@ -449,7 +459,7 @@ void PathState::setExpanded( if (!umForward.insert(std::make_pair(boost::make_tuple(pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second) { // Failed to insert. Have a loop. - cLog(lsDEBUG) << boost::str(boost::format("PathState: loop detected: %s") + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: loop detected: %s") % getJson()); terStatus = temBAD_PATH_LOOP; @@ -457,7 +467,7 @@ void PathState::setExpanded( } } - cLog(lsDEBUG) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s") + cLog(lsDEBUG) << boost::str(boost::format("setExpanded: in=%s/%s out=%s/%s %s") % STAmount::createHumanCurrency(uMaxCurrencyID) % RippleAddress::createHumanAccountID(uMaxIssuerID) % STAmount::createHumanCurrency(uOutCurrencyID) @@ -2612,6 +2622,10 @@ TER RippleCalc::rippleCalc( { RippleCalc rc(lesActive, bOpenLedger); + cLog(lsTRACE) << boost::str(boost::format("rippleCalc> saMaxAmountReq=%s saDstAmountReq=%s") + % saMaxAmountReq.getFullText() + % saDstAmountReq.getFullText()); + TER terResult = temUNCERTAIN; // YYY Might do basic checks on src and dst validity as per doPayment. @@ -2669,6 +2683,12 @@ int iIndex = 0; if (!pspExpanded) return temUNKNOWN; + cLog(lsTRACE) << boost::str(boost::format("rippleCalc: EXPAND: saDstAmountReq=%s saMaxAmountReq=%s uDstAccountID=%s uSrcAccountID=%s") + % saDstAmountReq.getFullText() + % saMaxAmountReq.getFullText() + % RippleAddress::createHumanAccountID(uDstAccountID) + % RippleAddress::createHumanAccountID(uSrcAccountID)); + pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID); cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s") @@ -2764,6 +2784,7 @@ int iPass = 0; } } } + if (sLog(lsDEBUG)) { cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size()); @@ -3003,7 +3024,7 @@ void TransactionEngine::calcOfferBridgeNext( { // Offer must be redeeming IOUs. - // No additional + // No additional // XXX Broken } diff --git a/src/cpp/ripple/RippleCalc.h b/src/cpp/ripple/RippleCalc.h index 8df0eebba9..c23f2be837 100644 --- a/src/cpp/ripple/RippleCalc.h +++ b/src/cpp/ripple/RippleCalc.h @@ -209,8 +209,8 @@ public: const bool bPartialPayment, const bool bLimitQuality, const bool bNoRippleDirect, - const bool bStandAlone, - const bool bOpenLedger = true + const bool bStandAlone, // --> True, not to affect accounts. + const bool bOpenLedger = true // --> What kind of errors to return. ); static void setCanonical(STPathSet& spsDst, const std::vector& vpsExpanded, bool bKeepDefault); diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 22995fdee4..676290ccad 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -15,6 +15,10 @@ #include "SHAMap.h" #include "Application.h" +#ifndef STATE_MAP_BUCKETS +#define STATE_MAP_BUCKETS 1024 +#endif + SETUP_LOG(); DECLARE_INSTANCE(SHAMap); @@ -52,6 +56,8 @@ std::size_t hash_value(const uint160& u) SHAMap::SHAMap(SHAMapType t, uint32 seq) : mSeq(seq), mState(smsModifying), mType(t) { + if (t == smtSTATE) + mTNByID.rehash(STATE_MAP_BUCKETS); root = boost::make_shared(mSeq, SHAMapNode(0, uint256())); root->makeInner(); mTNByID[*root] = root; @@ -59,6 +65,8 @@ SHAMap::SHAMap(SHAMapType t, uint32 seq) : mSeq(seq), mState(smsModifying), mTyp SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(1), mState(smsSynching), mType(t) { // FIXME: Need to acquire root node + if (t == smtSTATE) + mTNByID.rehash(STATE_MAP_BUCKETS); root = boost::make_shared(mSeq, SHAMapNode(0, uint256())); root->makeInner(); mTNByID[*root] = root; @@ -205,10 +213,10 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& has #ifdef DEBUG if (node->getNodeHash() != hash) { - std::cerr << "Attempt to get node, hash not in tree" << std::endl; - std::cerr << "ID: " << id << std::endl; - std::cerr << "TgtHash " << hash << std::endl; - std::cerr << "NodHash " << node->getNodeHash() << std::endl; + cLog(lsFATAL) << "Attempt to get node, hash not in tree"; + cLog(lsFATAL) << "ID: " << id; + cLog(lsFATAL) << "TgtHash " << hash; + cLog(lsFATAL) << "NodHash " << node->getNodeHash(); throw std::runtime_error("invalid node"); } #endif @@ -264,9 +272,6 @@ SHAMapItem::SHAMapItem(const uint256& tag, const Serializer& data) SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node) { // Return the first item below this node -#ifdef ST_DEBUG - std::cerr << "firstBelow(" << *node << ")" << std::endl; -#endif do { // Walk down the tree if (node->hasItem()) return node; @@ -275,11 +280,6 @@ SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node) for (int i = 0; i < 16; ++i) if (!node->isEmptyBranch(i)) { -#ifdef ST_DEBUG - std::cerr << " FB: node " << *node << std::endl; - std::cerr << " has non-empty branch " << i << " : " << - node->getChildNodeID(i) << ", " << node->getChildHash(i) << std::endl; -#endif node = getNodePointer(node->getChildNodeID(i), node->getChildHash(i)); foundNode = true; break; @@ -291,10 +291,6 @@ SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node) SHAMapTreeNode* SHAMap::lastBelow(SHAMapTreeNode* node) { -#ifdef DEBUG - std::cerr << "lastBelow(" << *node << ")" << std::endl; -#endif - do { // Walk down the tree if (node->hasItem()) @@ -330,7 +326,7 @@ SHAMapItem::pointer SHAMap::onlyBelow(SHAMapTreeNode* node) if (!nextNode) { - std::cerr << *node << std::endl; + cLog(lsFATAL) << *node; assert(false); return SHAMapItem::pointer(); } @@ -542,9 +538,6 @@ bool SHAMap::delItem(const uint256& id) int bc = node->getBranchCount(); if (bc == 0) { -#ifdef DEBUG - std::cerr << "delItem makes empty node" << std::endl; -#endif prevHash=uint256(); if (!mTNByID.erase(*node)) assert(false); @@ -556,9 +549,6 @@ bool SHAMap::delItem(const uint256& id) { returnNode(node, true); eraseChildren(node); -#ifdef ST_DEBUG - std::cerr << "Making item node " << *node << std::endl; -#endif node->setItem(item, type); } prevHash = node->getNodeHash(); @@ -577,10 +567,6 @@ bool SHAMap::delItem(const uint256& id) bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) { // add the specified item, does not update -#ifdef ST_DEBUG - std::cerr << "aGI " << item->getTag() << std::endl; -#endif - uint256 tag = item->getTag(); SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); @@ -603,17 +589,14 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) if (node->isInner()) { // easy case, we end on an inner node -#ifdef ST_DEBUG - std::cerr << "aGI inner " << *node << std::endl; -#endif int branch = node->selectBranch(tag); assert(node->isEmptyBranch(branch)); SHAMapTreeNode::pointer newNode = boost::make_shared(node->getChildNodeID(branch), item, type, mSeq); if (!mTNByID.emplace(SHAMapNode(*newNode), newNode).second) { - std::cerr << "Node: " << *node << std::endl; - std::cerr << "NewNode: " << *newNode << std::endl; + cLog(lsFATAL) << "Node: " << *node; + cLog(lsFATAL) << "NewNode: " << *newNode; dump(); assert(false); throw std::runtime_error("invalid inner node"); @@ -623,10 +606,6 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) } else { // this is a leaf node that has to be made an inner node holding two items -#ifdef ST_DEBUG - std::cerr << "aGI leaf " << *node << std::endl; - std::cerr << "Existing: " << node->peekItem()->getTag() << std::endl; -#endif SHAMapItem::pointer otherItem = node->peekItem(); assert(otherItem && (tag != otherItem->getTag())); @@ -636,10 +615,6 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) while ((b1 = node->selectBranch(tag)) == (b2 = node->selectBranch(otherItem->getTag()))) { // we need a new inner node, since both go on same branch at this level -#ifdef ST_DEBUG - std::cerr << "need new inner node at " << node->getDepth() << ", " - << b1 << "==" << b2 << std::endl; -#endif SHAMapTreeNode::pointer newNode = boost::make_shared(mSeq, node->getChildNodeID(b1)); newNode->makeInner(); @@ -711,7 +686,7 @@ bool SHAMap::updateGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMe void SHAMapItem::dump() { - std::cerr << "SHAMapItem(" << mTag << ") " << mData.size() << "bytes" << std::endl; + cLog(lsINFO) << "SHAMapItem(" << mTag << ") " << mData.size() << "bytes"; } SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const uint256& hash) @@ -729,7 +704,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui try { SHAMapTreeNode::pointer ret = - boost::make_shared(id, obj->getData(), mSeq, snfPREFIX, hash); + boost::make_shared(id, obj->getData(), mSeq, snfPREFIX, hash, true); if (id != *ret) { cLog(lsFATAL) << "id:" << id << ", got:" << *ret; @@ -880,25 +855,13 @@ void SHAMap::dropCache() void SHAMap::dump(bool hash) { -#if 0 - std::cerr << "SHAMap::dump" << std::endl; - SHAMapItem::pointer i=peekFirstItem(); - while (i) - { - std::cerr << "Item: id=" << i->getTag() << std::endl; - i = peekNextItem(i->getTag()); - } - std::cerr << "SHAMap::dump done" << std::endl; -#endif - - std::cerr << " MAP Contains" << std::endl; + cLog(lsINFO) << " MAP Contains"; boost::recursive_mutex::scoped_lock sl(mLock); for(boost::unordered_map::iterator it = mTNByID.begin(); it != mTNByID.end(); ++it) { - std::cerr << it->second->getString() << std::endl; - if (hash) - std::cerr << " " << it->second->getNodeHash() << std::endl; + cLog(lsINFO) << it->second->getString(); + tLog(hash, lsINFO) << it->second->getNodeHash(); } } diff --git a/src/cpp/ripple/SHAMap.h b/src/cpp/ripple/SHAMap.h index 30ad9e52d7..d241f18432 100644 --- a/src/cpp/ripple/SHAMap.h +++ b/src/cpp/ripple/SHAMap.h @@ -187,7 +187,7 @@ public: // raw node functions SHAMapTreeNode(const SHAMapNode& id, const std::vector& data, uint32 seq, - SHANodeFormat format, const uint256& hash); + SHANodeFormat format, const uint256& hash, bool hashValid); void addRaw(Serializer &, SHANodeFormat format); virtual bool isPopulated() const { return true; } diff --git a/src/cpp/ripple/SHAMapNodes.cpp b/src/cpp/ripple/SHAMapNodes.cpp index ace6a10e1f..6989e80928 100644 --- a/src/cpp/ripple/SHAMapNodes.cpp +++ b/src/cpp/ripple/SHAMapNodes.cpp @@ -177,7 +177,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::ref item, TNT } SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& rawNode, uint32 seq, - SHANodeFormat format, const uint256& hash) : + SHANodeFormat format, const uint256& hash, bool hashValid) : SHAMapNode(id), mSeq(seq), mType(tnERROR), mIsBranch(0), mFullBelow(false) { if (format == snfWIRE) @@ -317,9 +317,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& nodeIDs, std::vector& hashes, int max, SHAMapSyncFilter* filter) { @@ -63,7 +65,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector= 1); SHAMapTreeNode::pointer ptr = - boost::make_shared(childID, nodeData, mSeq - 1, snfPREFIX, childHash); + boost::make_shared(childID, nodeData, mSeq - 1, snfPREFIX, childHash, true); cLog(lsTRACE) << "Got sync node from cache: " << *ptr; mTNByID[*ptr] = ptr; d = ptr.get(); @@ -213,7 +215,7 @@ SMAddNode SHAMap::addRootNode(const std::vector& rootNode, SHANod assert(mSeq >= 1); SHAMapTreeNode::pointer node = - boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uint256()); + boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uZero, false); if (!node) return SMAddNode::invalid(); @@ -253,7 +255,7 @@ SMAddNode SHAMap::addRootNode(const uint256& hash, const std::vector= 1); SHAMapTreeNode::pointer node = - boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uint256()); + boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uZero, false); if (!node || node->getNodeHash() != hash) return SMAddNode::invalid(); @@ -332,7 +334,7 @@ SMAddNode SHAMap::addKnownNode(const SHAMapNode& node, const std::vector= 1); SHAMapTreeNode::pointer newNode = - boost::make_shared(node, rawNode, mSeq - 1, snfWIRE, uint256()); + boost::make_shared(node, rawNode, mSeq - 1, snfWIRE, uZero, false); if (hash != newNode->getNodeHash()) // these aren't the droids we're looking for return SMAddNode::invalid(); diff --git a/src/cpp/ripple/TransactionCheck.cpp b/src/cpp/ripple/TransactionCheck.cpp new file mode 100644 index 0000000000..50b1a7f7cf --- /dev/null +++ b/src/cpp/ripple/TransactionCheck.cpp @@ -0,0 +1,11 @@ + +#include "TransactionErr.h" +#include "TransactionEngine.h" + +// Double check a transaction's metadata to make sure no system invariants were broken +// Call right before 'calcRawMeta' + +bool TransactionEngine::checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params) +{ + return true; +} diff --git a/src/cpp/ripple/TransactionEngine.cpp b/src/cpp/ripple/TransactionEngine.cpp index 1e0beac41d..1c0049fb20 100644 --- a/src/cpp/ripple/TransactionEngine.cpp +++ b/src/cpp/ripple/TransactionEngine.cpp @@ -93,7 +93,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa } #endif - UPTR_T transactor = Transactor::makeTransactor(txn,params,this); + UPTR_T transactor = Transactor::makeTransactor(txn, params, this); if (transactor.get() != NULL) { uint256 txID = txn.getTransactionID(); @@ -153,28 +153,40 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa if (didApply) { - // Transaction succeeded fully or (retries are not allowed and the transaction could claim a fee) - Serializer m; - mNodes.calcRawMeta(m, terResult, mTxnSeq++); - - txnWrite(); - - Serializer s; - txn.add(s); - - if (isSetBit(params, tapOPEN_LEDGER)) + if (!checkInvariants(terResult, txn, params)) { - if (!mLedger->addTransaction(txID, s)) - assert(false); + cLog(lsFATAL) << "Transaction violates invariants"; + cLog(lsFATAL) << txn.getJson(0); + cLog(lsFATAL) << transToken(terResult) << ": " << transHuman(terResult); + cLog(lsFATAL) << mNodes.getJson(0); + didApply = false; + terResult = tefINTERNAL; } else { - if (!mLedger->addTransaction(txID, s, m)) + // Transaction succeeded fully or (retries are not allowed and the transaction could claim a fee) + Serializer m; + mNodes.calcRawMeta(m, terResult, mTxnSeq++); + + txnWrite(); + + Serializer s; + txn.add(s); + + if (isSetBit(params, tapOPEN_LEDGER)) + { + if (!mLedger->addTransaction(txID, s)) + assert(false); + } + else + { + if (!mLedger->addTransaction(txID, s, m)) assert(false); - // Charge whatever fee they specified. - STAmount saPaid = txn.getTransactionFee(); - mLedger->destroyCoins(saPaid.getNValue()); + // Charge whatever fee they specified. + STAmount saPaid = txn.getTransactionFee(); + mLedger->destroyCoins(saPaid.getNValue()); + } } } diff --git a/src/cpp/ripple/TransactionEngine.h b/src/cpp/ripple/TransactionEngine.h index 2ad074efb6..17e1e02f94 100644 --- a/src/cpp/ripple/TransactionEngine.h +++ b/src/cpp/ripple/TransactionEngine.h @@ -65,6 +65,7 @@ public: void entryModify(SLE::ref sleEntry) { mNodes.entryModify(sleEntry); } TER applyTransaction(const SerializedTransaction&, TransactionEngineParams, bool& didApply); + bool checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params); }; inline TransactionEngineParams operator|(const TransactionEngineParams& l1, const TransactionEngineParams& l2) diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index 160064ffeb..1709cfc368 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -38,6 +38,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." }, { tefNO_AUTH_REQUIRED, "tefNO_AUTH_REQUIRED", "Auth is not required." }, { tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past." }, + { tefINTERNAL, "tefINTERNAL", "Internal error." }, { telLOCAL_ERROR, "telLOCAL_ERROR", "Local failure." }, { telBAD_DOMAIN, "telBAD_DOMAIN", "Domain too long." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index fe54d9f4bb..186aa3c145 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -81,6 +81,7 @@ enum TER // aka TransactionEngineResult tefGEN_IN_USE, tefNO_AUTH_REQUIRED, // Can't set auth if auth is not required. tefPAST_SEQ, + tefINTERNAL, // -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent) // Causes: diff --git a/src/cpp/ripple/base58.h b/src/cpp/ripple/base58.h index 5f08f48718..35a6acfef1 100644 --- a/src/cpp/ripple/base58.h +++ b/src/cpp/ripple/base58.h @@ -164,11 +164,8 @@ protected: unsigned char nVersion; std::vector vchData; - CBase58Data() - { - nVersion = 1; - vchData.clear(); - } + CBase58Data() : nVersion(1) + { ; } ~CBase58Data() { @@ -247,13 +244,7 @@ public: friend std::size_t hash_value(const CBase58Data& b58); }; -inline std::size_t hash_value(const CBase58Data& b58) -{ - std::size_t seed = boost::hash_value(b58.nVersion); +extern std::size_t hash_value(const CBase58Data& b58); - boost::hash_combine(seed, b58.vchData); - - return seed; -} #endif // vim:ts=4 diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 8a7ed48b24..239d8301ac 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -70,11 +70,10 @@ void printHelp(const po::options_description& desc) cerr << desc << endl; cerr << "Commands: " << endl; - cerr << " account_info |" << endl; - cerr << " account_info || []" << endl; - cerr << " account_lines || []" << endl; - cerr << " account_offers || []" << endl; - cerr << " account_tx || |( )" << endl; + cerr << " account_info |||| []" << endl; + cerr << " account_lines || []" << endl; + cerr << " account_offers || []" << endl; + cerr << " account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending]" << endl; cerr << " book_offers [ [ [ []]]]]" << endl; cerr << " connect []" << endl; cerr << " consensus_info" << endl; @@ -188,8 +187,17 @@ int main(int argc, char* argv[]) } } + if (iResult) + { + nothing(); + } + else if (vm.count("help")) + { + iResult = 1; + } + if (HaveSustain() && - !vm.count("parameters") && !vm.count("fg") && !vm.count("standalone") && !vm.count("unittest")) + !iResult && !vm.count("parameters") && !vm.count("fg") && !vm.count("standalone") && !vm.count("unittest")) { std::string logMe = DoSustain(); if (!logMe.empty()) @@ -251,16 +259,13 @@ int main(int argc, char* argv[]) { nothing(); } - else if (vm.count("help")) - { - iResult = 1; - } else if (!vm.count("parameters")) { // No arguments. Run server. setupServer(); NameThread("io"); startServer(); + InstanceType::shutdown(); } else { diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp index a8bbaa75a6..fb488f19ff 100644 --- a/src/cpp/ripple/utils.cpp +++ b/src/cpp/ripple/utils.cpp @@ -4,6 +4,10 @@ #include #include #endif +#ifdef __FreeBSD__ +#include +#include +#endif #include @@ -20,6 +24,9 @@ void getRand(unsigned char *buf, int num) { +#ifdef PURIFY + memset(buf, 0, num); +#endif if (RAND_bytes(buf, num) != 1) { assert(false); @@ -336,8 +343,8 @@ uint32_t htobe32(uint32_t value) } uint32_t be32toh(uint32_t value) -{ - return( _byteswap_ulong(value)); +{ + return( _byteswap_ulong(value)); } #endif @@ -443,8 +450,8 @@ std::string DoSustain() #else bool HaveSustain() { return false; } -std::string DoSustain() { return std::string; } -std::string StopSustain() { return std::string; } +std::string DoSustain() { return std::string(); } +std::string StopSustain() { return std::string(); } #endif diff --git a/src/js/network.js b/src/js/network.js index 714aa335f1..3f7697a995 100644 --- a/src/js/network.js +++ b/src/js/network.js @@ -48,7 +48,7 @@ Network.protocol.start = function () { }; -// Target state: disconnectted +// Target state: disconnect Network.protocol.stop = function () { }; diff --git a/test/server.js b/test/server.js index 5b9e639586..bb0936ca3a 100644 --- a/test/server.js +++ b/test/server.js @@ -192,7 +192,8 @@ Server.prototype.stop = function () { if (!self.quiet) console.log("server: stop: server exited"); self.emit('stopped'); - delete this.child; + + delete self.child; }); this.child.kill(); diff --git a/test/testutils.js b/test/testutils.js index b81df48612..39495f213a 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -129,14 +129,14 @@ var build_teardown = function (host) { .connect(false); }, function stopServerStep(callback) { - if (opts.no_server) { + return callback(); + } - return callback(); - } - - data.server.on('stopped', callback).stop(); + data.server + .on('stopped', callback) + .stop(); } ], done); };